Compare commits

...

47 commits

Author SHA1 Message Date
sirdabalot
8bdfc43fba Rewrote fletching 2025-11-29 00:16:56 +11:00
oftheshire
12f217f8f3 Tortoise no longer drop 3 regular-sized bones on death
Tortoises have corrected HP (101 for level 79, 121 for level 92)
Added stats and animations for Gnome Driver, Gnome Mage, and Gnome Archer
Gnome-Mounted Tortoise now attacks with its mounted gnomes, and the gnomes spawn on the ground upon tortoise death
2025-11-28 23:43:00 +11:00
dam
07e48d91a8 Fixed the price of spirit shards and pouches 2025-11-28 23:37:26 +11:00
dam
0bf2f7d0ec Corrected destination coordinates of the Carrallangar teleport 2025-11-28 23:35:26 +11:00
Player Name
a38d3734bd Global news announcements no longer overrun the chatbox
Global news announcements are now locked behind the enable_global_chat server config setting
PvP and brawler drops from the Chaos Elemental now refer to it as "the Chaos Elemental", rather than "a Chaos Elemental"
PvP and brawler drops from revenants now lowercase the revenant's name
2025-11-27 23:18:27 +11:00
Player Name
96c42c18d9 Added a new server config option, shooting_star_ring, enabling whether the inauthentic ancient blueprint gets rolled 2025-11-27 23:08:58 +11:00
dam
1765f3a3b6 Fixed ice spell casts allowing frozen victim movement for 1 tick
Fixed ice barrage animations
Players who get successfully frozen by an ice spell now receive the message "You have been frozen!"
2025-11-27 23:04:25 +11:00
Player Name
5533a2f78a Fixed discord mod message logging
Fixed a typo when attempting to sell an item on the GE that is blacklisted
Implemented grinding of suqah teeth
2025-11-27 23:03:17 +11:00
oftheshire
846d55365a Fixed Wyson's dialogue so that he properly respects the choice of turning in mole parts
Corrected dialogue to authentic source
2025-11-27 23:01:33 +11:00
Syndromeramo
da7c62d10c Implemented Observatory Quest 2025-11-27 22:59:02 +11:00
Player Name
2239168074 Corrected the retreat mechanic to not heal the npc 2025-11-27 22:56:04 +11:00
Player Name
8ba45e0ee1 Corrected the deep wildy murder message to the authentic one
Fixed a bug that caused the prayer-recharge jingle to be played over the death jingle when the player died
Fixed redundant overload checks
Fixed lunar spells consuming double runes when a previous cast attempt failed
Fixed HP on halloween random-event spiders
Corrected the Dream spell logic
2025-11-27 22:55:00 +11:00
Player Name
82e5965220 Implemented periodic Grand Exchange update notifications 2025-11-27 22:48:23 +11:00
Player Name
3d5cbd90ed Improved random event kidnapping logic
Rewrote home teleport
Corrected shades and zombies to only multiply xp by 1/16 if they were random events
Unified non-hostile random event chat mechanics
Fixed random events repeating their opening message and/or saying their timeout message prematurely
Genie will now authentically address female players as 'Mistress'
Fixed hostile randoms wrongly teleporting the player after 3 minutes
Made quizmaster dialogues unclosable and added a hook to restart them if you lose it
Made the pillory dialog unclosable
Implemented rock golem switch between ranged and melee
Replaced sandwich lady chat lines with authentic quotes
Added authentic dialogue for interacting with another player's sandwich lady
Added a teleport block for the surprise exam random event
2025-11-27 22:44:43 +11:00
Poseidon
feb2b10247 Fixed Ghost Disciple dialogue typo at Ectofuntus 2025-11-27 22:10:23 +11:00
Kennynes
56986ad6bc Corrected rune essence mining behavior and speeds
Also corrected various mining messages across many rock types to be authentic
2025-11-26 00:16:48 +11:00
dam
6241ff9ce5 Improved pathfinding to handle cases when both player and target are moving 2025-11-26 00:14:40 +11:00
Player Name
dfb53cfa03 Made XP rates and ironman optional, locked behind new server config settings xp_rates and ironman
Removed the ironman_icons server config setting, locking it behind ironman instead
Removed the default_xp_rate server config setting and hardcoded the default xp rate to 1x
Changed a bunch of server config defaults for inauthentic features to disabled
Added enable_global_chat to the worldprops default config. Note the change from enable_globalchat to enable_global_chat
Removed the allow_slayer_rerolls worldprops config option
Large number of tutorial island improvements and authenticity enhancements
2025-11-26 00:12:56 +11:00
Kennynes
66030f36d6 Evil Bob's fishing event cutscene now much more authentic 2025-11-26 00:01:34 +11:00
oftheshire
8dd82dd356 Light source is now required to get boots of lightness
Boots of lightness now work as expected
2025-11-26 00:00:31 +11:00
Player Name
1fa2d3460f Unified and reimplemented various banker dialogues
Accessibility to second bank is now locked behind server config toggle second_bank
Eniola, Arnold Lydspor, and Emerald Benedict now offer access to second bank
Locked the backported ability to charge our ring of wealth with GE teleports behind server config toggle ring_of_wealth_teleport
Corrected enchant spells to always yield uncharged dragonstone items, rather than fully charged ones
Fixed inauthentic jewellery UI
Locked the availability of our inauthentic player commands behind server config toggle player_commands. Admins are immune to this and still always have all commands available. Note that this includes ::confirmrules, so be sure not to set your server to show rules and info, but not allow commands
2025-11-25 23:56:42 +11:00
Player Name
78f1c58b4e Fixed the remaining dramen tree inauthenticity, now provides reward after a single tick of cutting 2025-11-25 19:36:36 +11:00
dam
9a7a885818 Barrows equipment now degrades by 20% on grave death (fully repaired equipment is excluded from this)
Barrows equipment now breaks and drops on non-grave deaths (instead of converting to coins)
Rewrote Bob handlers
2025-11-25 19:12:24 +11:00
Player Name
8d182f2505 Corrected the Ape Atoll teleport location
Added Solihib npc spawn
Implemented Solihib's store
2025-11-25 19:04:03 +11:00
dam
f25f439bdf Fixed female characters not being able to pay Falador Hairdresser hairstyle change fee 2025-11-25 18:36:01 +11:00
oftheshire
4b3cfae309 Fixed typo in Larxus's dialogue (champion's challenge) 2025-11-25 18:28:09 +11:00
Ryan
e6b1203be4 Fixed IP address not getting written to DB on login 2025-11-25 18:10:02 +11:00
Syndromeramo
8694d36ee8 Another round of Entrana allowed item auditing, now much more authentic 2025-11-10 23:33:06 +11:00
Kennynes
618baf0662 Rewrote nettle tea handlers, additionally allowing emptying of cup of tea 2025-11-10 23:30:03 +11:00
Kennynes
f1f5a97859 Made the Black Demon cutscene more authentic and fixed Glough's chathead in the cutscene 2025-11-10 23:28:13 +11:00
Oven Bread
991d4aa364 Added some newly found examine texts 2025-11-10 23:24:45 +11:00
Player Name
789466e4ab Fixed incorrect ordering of operations when opening the bank
Somewhat improved sending of tab configurations and free space
2025-11-10 23:10:44 +11:00
Oven Bread
f9a193c8d7 Implemented the castle wars manual book 2025-11-10 22:43:16 +11:00
Ceikry
d1cfe299c6 Add default issue template now that Gitlab has changed how things work 2025-10-17 22:07:14 +00:00
Syndromeramo
7a23f6f0a3 First phase of TzHaar rewrite 2025-10-08 00:41:11 +11:00
Syndromeramo
c135877680 Catching a horned graahk now finishes the appropriate Karamja task
Checking the health of a fruit tree in Brimhaven now finishes the appropriate Karamja task
2025-10-08 00:19:49 +11:00
damighty
18f274be8e Implemented search command, ::commandsearch <query> 2025-10-08 00:16:28 +11:00
Syndromeramo
5719d03e4d Farmed trees will now correctly regrow based on the same timer as other trees, rather than being tied to growth cycles
Implemented admin ::instachop command
2025-10-07 23:46:14 +11:00
Syndromeramo
d6f32c56fc Chinchompa long fuse now properly splits ranged/defense XP 2025-10-07 23:45:33 +11:00
Syndromeramo
9eac712626 Fixed Cabin Fever charter discount; awarded via quest requirement system
Fixed charter boat exception
Fixed Mos Le'Harmless charter boat discount and prices
2025-10-07 23:08:27 +11:00
Player Name
b644a72bfc Corrected poison dagger attack animations for all metal daggers 2025-10-07 22:50:55 +11:00
oftheshire
efe936500b Fixed potato cactus spawns
Fixed 3 spawns in Kalphite Lair first floor
Fixed 5 spawns in Kalphite Lair bottom floor
2025-09-27 11:08:34 +10:00
Syndromeramo
6d8ef4247c Poison corrections
::poison command (admin) will now automatically remove poison immunity timers for faster testing
Poison will no longer give an inauthentic warning when it is about to expire
Newly applied poison timers will only overwrite old poison timers if they are greater in severity
Fixed up some logic for monster examine checking poison immunity
Fixed numerous poison stats
2025-09-23 23:35:52 +10:00
Bishop
676397f3fd Fixed bug causing herb and uncommon seed drop tables to only roll once when they should have rolled multiple times 2025-09-02 20:43:02 +10:00
Syndromeramo
74cbec067c Made Bork poison immune 2025-09-02 15:06:37 +10:00
Oven Bread
d03df027e0 Fixed minor regression in dialogue code causing "line1" to appear briefly before dialogue is shown 2025-09-01 20:26:00 +10:00
Syndromeramo
d09bdacf94 Fixed bug where All Fired Up beacons could be repaired with insufficient items
Fixed bug where All Fired Up beacons would consider boosted/dynamic skill level instead of actual skill level
Removed random inauthentic needle break when repairing All Fired Up beacon
2025-09-01 20:20:25 +10:00
253 changed files with 9433 additions and 8705 deletions

0
.gitlab/.gitkeep Normal file
View file

View file

View file

@ -0,0 +1,17 @@
What I did:
What I expected to happen:
What actually happened:
IDs of related NPCs/items:
2009-era source (if relevant):
Screenshots or video:
LIVE SERVER username affected by this issue:
**Bug reports are not accepted by SP users. SP is often out of date and results in invalid bug reports.**
**If the bug is exploitable make sure you tick the confidential checkbox below**

View file

@ -125,7 +125,7 @@
"start_graphic": "232,96", "start_graphic": "232,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "226,40,36,32,32,15,0", "projectile": "226,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "813", "itemId": "813",
@ -133,7 +133,7 @@
"start_graphic": "233,96", "start_graphic": "233,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "227,40,36,32,32,15,0", "projectile": "227,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "814", "itemId": "814",
@ -141,7 +141,7 @@
"start_graphic": "234,96", "start_graphic": "234,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "228,40,36,32,32,15,0", "projectile": "228,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "815", "itemId": "815",
@ -149,7 +149,7 @@
"start_graphic": "235,96", "start_graphic": "235,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "229,40,36,32,32,15,0", "projectile": "229,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "816", "itemId": "816",
@ -157,7 +157,7 @@
"start_graphic": "236,96", "start_graphic": "236,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "230,40,36,32,32,15,0", "projectile": "230,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "817", "itemId": "817",
@ -165,7 +165,7 @@
"start_graphic": "237,96", "start_graphic": "237,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "231,40,36,32,32,15,0", "projectile": "231,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "825", "itemId": "825",
@ -221,7 +221,7 @@
"start_graphic": "206,96", "start_graphic": "206,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "200,40,36,32,32,15,0", "projectile": "200,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "832", "itemId": "832",
@ -229,7 +229,7 @@
"start_graphic": "207,96", "start_graphic": "207,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "201,40,36,32,32,15,0", "projectile": "201,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "833", "itemId": "833",
@ -237,7 +237,7 @@
"start_graphic": "208,96", "start_graphic": "208,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "202,40,36,32,32,15,0", "projectile": "202,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "834", "itemId": "834",
@ -245,7 +245,7 @@
"start_graphic": "209,96", "start_graphic": "209,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "203,40,36,32,32,15,0", "projectile": "203,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "835", "itemId": "835",
@ -253,7 +253,7 @@
"start_graphic": "210,96", "start_graphic": "210,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "204,40,36,32,32,15,0", "projectile": "204,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "836", "itemId": "836",
@ -261,7 +261,7 @@
"start_graphic": "211,96", "start_graphic": "211,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "205,40,36,32,32,15,0", "projectile": "205,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "863", "itemId": "863",
@ -325,7 +325,7 @@
"start_graphic": "219,96", "start_graphic": "219,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "212,40,36,32,32,15,0", "projectile": "212,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "871", "itemId": "871",
@ -333,7 +333,7 @@
"start_graphic": "220,96", "start_graphic": "220,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "213,40,36,32,32,15,0", "projectile": "213,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "872", "itemId": "872",
@ -341,7 +341,7 @@
"start_graphic": "221,96", "start_graphic": "221,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "214,40,36,32,32,15,0", "projectile": "214,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "873", "itemId": "873",
@ -349,7 +349,7 @@
"start_graphic": "223,96", "start_graphic": "223,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "216,40,36,32,32,15,0", "projectile": "216,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "874", "itemId": "874",
@ -357,7 +357,7 @@
"start_graphic": "222,96", "start_graphic": "222,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "215,40,36,32,32,15,0", "projectile": "215,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "875", "itemId": "875",
@ -365,7 +365,7 @@
"start_graphic": "224,96", "start_graphic": "224,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "217,40,36,32,32,15,0", "projectile": "217,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "876", "itemId": "876",
@ -373,7 +373,7 @@
"start_graphic": "225,96", "start_graphic": "225,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "218,40,36,32,32,15,0", "projectile": "218,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "877", "itemId": "877",
@ -389,7 +389,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "879", "itemId": "879",
@ -421,7 +421,7 @@
"start_graphic": "19,96", "start_graphic": "19,96",
"darkbow_graphic": "1104,96", "darkbow_graphic": "1104,96",
"projectile": "10,40,36,41,46,5,0", "projectile": "10,40,36,41,46,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "884", "itemId": "884",
@ -437,7 +437,7 @@
"start_graphic": "18,96", "start_graphic": "18,96",
"darkbow_graphic": "1105,96", "darkbow_graphic": "1105,96",
"projectile": "9,40,36,41,46,5,0", "projectile": "9,40,36,41,46,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "886", "itemId": "886",
@ -453,7 +453,7 @@
"start_graphic": "20,96", "start_graphic": "20,96",
"darkbow_graphic": "1106,96", "darkbow_graphic": "1106,96",
"projectile": "11,40,36,41,46,5,0", "projectile": "11,40,36,41,46,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "888", "itemId": "888",
@ -469,7 +469,7 @@
"start_graphic": "21,96", "start_graphic": "21,96",
"darkbow_graphic": "1107,96", "darkbow_graphic": "1107,96",
"projectile": "12,40,36,41,46,5,0", "projectile": "12,40,36,41,46,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "890", "itemId": "890",
@ -485,7 +485,7 @@
"start_graphic": "22,96", "start_graphic": "22,96",
"darkbow_graphic": "1108,96", "darkbow_graphic": "1108,96",
"projectile": "13,40,36,41,46,5,0", "projectile": "13,40,36,41,46,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "892", "itemId": "892",
@ -501,7 +501,7 @@
"start_graphic": "24,96", "start_graphic": "24,96",
"darkbow_graphic": "1109,96", "darkbow_graphic": "1109,96",
"projectile": "15,40,36,41,46,5,0", "projectile": "15,40,36,41,46,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "2532", "itemId": "2532",
@ -565,7 +565,7 @@
"start_graphic": "273,96", "start_graphic": "273,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "227,40,36,32,32,15,0", "projectile": "227,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "4160", "itemId": "4160",
@ -725,7 +725,7 @@
"start_graphic": "19,96", "start_graphic": "19,96",
"darkbow_graphic": "1104,96", "darkbow_graphic": "1104,96",
"projectile": "10,40,36,41,46,5,0", "projectile": "10,40,36,41,46,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5617", "itemId": "5617",
@ -733,7 +733,7 @@
"start_graphic": "18,96", "start_graphic": "18,96",
"darkbow_graphic": "1105,96", "darkbow_graphic": "1105,96",
"projectile": "9,40,36,41,46,5,0", "projectile": "9,40,36,41,46,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5618", "itemId": "5618",
@ -741,7 +741,7 @@
"start_graphic": "20,96", "start_graphic": "20,96",
"darkbow_graphic": "1106,96", "darkbow_graphic": "1106,96",
"projectile": "11,40,36,41,46,5,0", "projectile": "11,40,36,41,46,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5619", "itemId": "5619",
@ -749,7 +749,7 @@
"start_graphic": "21,96", "start_graphic": "21,96",
"darkbow_graphic": "1107,96", "darkbow_graphic": "1107,96",
"projectile": "12,40,36,41,46,5,0", "projectile": "12,40,36,41,46,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5620", "itemId": "5620",
@ -757,7 +757,7 @@
"start_graphic": "22,96", "start_graphic": "22,96",
"darkbow_graphic": "1108,96", "darkbow_graphic": "1108,96",
"projectile": "13,40,36,41,46,5,0", "projectile": "13,40,36,41,46,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5621", "itemId": "5621",
@ -773,7 +773,7 @@
"start_graphic": "19,96", "start_graphic": "19,96",
"darkbow_graphic": "1104,96", "darkbow_graphic": "1104,96",
"projectile": "10,40,36,41,46,5,0", "projectile": "10,40,36,41,46,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5623", "itemId": "5623",
@ -781,7 +781,7 @@
"start_graphic": "18,96", "start_graphic": "18,96",
"darkbow_graphic": "1105,96", "darkbow_graphic": "1105,96",
"projectile": "9,40,36,41,46,5,0", "projectile": "9,40,36,41,46,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5624", "itemId": "5624",
@ -789,7 +789,7 @@
"start_graphic": "20,96", "start_graphic": "20,96",
"darkbow_graphic": "1106,96", "darkbow_graphic": "1106,96",
"projectile": "11,40,36,41,46,5,0", "projectile": "11,40,36,41,46,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5625", "itemId": "5625",
@ -797,7 +797,7 @@
"start_graphic": "21,96", "start_graphic": "21,96",
"darkbow_graphic": "1107,96", "darkbow_graphic": "1107,96",
"projectile": "12,40,36,41,46,5,0", "projectile": "12,40,36,41,46,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5626", "itemId": "5626",
@ -805,7 +805,7 @@
"start_graphic": "22,96", "start_graphic": "22,96",
"darkbow_graphic": "1108,96", "darkbow_graphic": "1108,96",
"projectile": "13,40,36,41,46,5,0", "projectile": "13,40,36,41,46,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5627", "itemId": "5627",
@ -813,7 +813,7 @@
"start_graphic": "24,96", "start_graphic": "24,96",
"darkbow_graphic": "1109,96", "darkbow_graphic": "1109,96",
"projectile": "15,40,36,41,46,5,0", "projectile": "15,40,36,41,46,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5628", "itemId": "5628",
@ -821,7 +821,7 @@
"start_graphic": "232,96", "start_graphic": "232,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "226,40,36,32,32,15,0", "projectile": "226,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5629", "itemId": "5629",
@ -829,7 +829,7 @@
"start_graphic": "233,96", "start_graphic": "233,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "227,40,36,32,32,15,0", "projectile": "227,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5630", "itemId": "5630",
@ -837,7 +837,7 @@
"start_graphic": "235,96", "start_graphic": "235,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "229,40,36,32,32,15,0", "projectile": "229,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5631", "itemId": "5631",
@ -845,7 +845,7 @@
"start_graphic": "273,96", "start_graphic": "273,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "227,40,36,32,32,15,0", "projectile": "227,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5633", "itemId": "5633",
@ -853,7 +853,7 @@
"start_graphic": "236,96", "start_graphic": "236,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "230,40,36,32,32,15,0", "projectile": "230,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5634", "itemId": "5634",
@ -861,7 +861,7 @@
"start_graphic": "237,96", "start_graphic": "237,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "231,40,36,32,32,15,0", "projectile": "231,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5635", "itemId": "5635",
@ -869,7 +869,7 @@
"start_graphic": "232,96", "start_graphic": "232,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "226,40,36,32,32,15,0", "projectile": "226,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5636", "itemId": "5636",
@ -877,7 +877,7 @@
"start_graphic": "233,96", "start_graphic": "233,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "227,40,36,32,32,15,0", "projectile": "227,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5637", "itemId": "5637",
@ -885,7 +885,7 @@
"start_graphic": "234,96", "start_graphic": "234,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "228,40,36,32,32,15,0", "projectile": "228,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5638", "itemId": "5638",
@ -893,7 +893,7 @@
"start_graphic": "273,96", "start_graphic": "273,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "227,40,36,32,32,15,0", "projectile": "227,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5639", "itemId": "5639",
@ -901,7 +901,7 @@
"start_graphic": "235,96", "start_graphic": "235,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "229,40,36,32,32,15,0", "projectile": "229,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5640", "itemId": "5640",
@ -909,7 +909,7 @@
"start_graphic": "236,96", "start_graphic": "236,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "230,40,36,32,32,15,0", "projectile": "230,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5641", "itemId": "5641",
@ -917,7 +917,7 @@
"start_graphic": "237,96", "start_graphic": "237,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "231,40,36,32,32,15,0", "projectile": "231,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5642", "itemId": "5642",
@ -925,7 +925,7 @@
"start_graphic": "206,96", "start_graphic": "206,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "200,40,36,32,32,15,0", "projectile": "200,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5643", "itemId": "5643",
@ -933,7 +933,7 @@
"start_graphic": "207,96", "start_graphic": "207,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "201,40,36,32,32,15,0", "projectile": "201,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5644", "itemId": "5644",
@ -941,7 +941,7 @@
"start_graphic": "208,96", "start_graphic": "208,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "202,40,36,32,32,15,0", "projectile": "202,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5645", "itemId": "5645",
@ -949,7 +949,7 @@
"start_graphic": "209,96", "start_graphic": "209,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "203,40,36,32,32,15,0", "projectile": "203,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5646", "itemId": "5646",
@ -957,7 +957,7 @@
"start_graphic": "210,96", "start_graphic": "210,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "204,40,36,32,32,15,0", "projectile": "204,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5647", "itemId": "5647",
@ -965,7 +965,7 @@
"start_graphic": "211,96", "start_graphic": "211,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "205,40,36,32,32,15,0", "projectile": "205,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5648", "itemId": "5648",
@ -973,7 +973,7 @@
"start_graphic": "206,96", "start_graphic": "206,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "200,40,36,32,32,15,0", "projectile": "200,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5649", "itemId": "5649",
@ -981,7 +981,7 @@
"start_graphic": "207,96", "start_graphic": "207,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "201,40,36,32,32,15,0", "projectile": "201,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5650", "itemId": "5650",
@ -989,7 +989,7 @@
"start_graphic": "208,96", "start_graphic": "208,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "202,40,36,32,32,15,0", "projectile": "202,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5651", "itemId": "5651",
@ -997,7 +997,7 @@
"start_graphic": "209,96", "start_graphic": "209,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "203,40,36,32,32,15,0", "projectile": "203,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5652", "itemId": "5652",
@ -1005,7 +1005,7 @@
"start_graphic": "210,96", "start_graphic": "210,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "204,40,36,32,32,15,0", "projectile": "204,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5653", "itemId": "5653",
@ -1013,7 +1013,7 @@
"start_graphic": "211,96", "start_graphic": "211,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "205,40,36,32,32,15,0", "projectile": "205,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5654", "itemId": "5654",
@ -1021,7 +1021,7 @@
"start_graphic": "219,96", "start_graphic": "219,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "212,40,36,32,32,15,0", "projectile": "212,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5655", "itemId": "5655",
@ -1029,7 +1029,7 @@
"start_graphic": "220,96", "start_graphic": "220,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "213,40,36,32,32,15,0", "projectile": "213,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5656", "itemId": "5656",
@ -1037,7 +1037,7 @@
"start_graphic": "221,96", "start_graphic": "221,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "214,40,36,32,32,15,0", "projectile": "214,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5657", "itemId": "5657",
@ -1045,7 +1045,7 @@
"start_graphic": "223,96", "start_graphic": "223,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "216,40,36,32,32,15,0", "projectile": "216,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5658", "itemId": "5658",
@ -1053,7 +1053,7 @@
"start_graphic": "222,96", "start_graphic": "222,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "215,40,36,32,32,15,0", "projectile": "215,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5659", "itemId": "5659",
@ -1061,7 +1061,7 @@
"start_graphic": "224,96", "start_graphic": "224,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "217,40,36,32,32,15,0", "projectile": "217,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5660", "itemId": "5660",
@ -1069,7 +1069,7 @@
"start_graphic": "225,96", "start_graphic": "225,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "218,40,36,32,32,15,0", "projectile": "218,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "5661", "itemId": "5661",
@ -1077,7 +1077,7 @@
"start_graphic": "219,96", "start_graphic": "219,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "212,40,36,32,32,15,0", "projectile": "212,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5662", "itemId": "5662",
@ -1085,7 +1085,7 @@
"start_graphic": "220,96", "start_graphic": "220,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "213,40,36,32,32,15,0", "projectile": "213,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5663", "itemId": "5663",
@ -1093,7 +1093,7 @@
"start_graphic": "221,96", "start_graphic": "221,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "214,40,36,32,32,15,0", "projectile": "214,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5664", "itemId": "5664",
@ -1101,7 +1101,7 @@
"start_graphic": "223,96", "start_graphic": "223,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "216,40,36,32,32,15,0", "projectile": "216,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5665", "itemId": "5665",
@ -1109,7 +1109,7 @@
"start_graphic": "222,96", "start_graphic": "222,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "215,40,36,32,32,15,0", "projectile": "215,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5666", "itemId": "5666",
@ -1117,7 +1117,7 @@
"start_graphic": "224,96", "start_graphic": "224,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "217,40,36,32,32,15,0", "projectile": "217,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "5667", "itemId": "5667",
@ -1125,7 +1125,7 @@
"start_graphic": "225,96", "start_graphic": "225,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "218,40,36,32,32,15,0", "projectile": "218,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "6061", "itemId": "6061",
@ -1133,7 +1133,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "6062", "itemId": "6062",
@ -1141,7 +1141,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "6522", "itemId": "6522",
@ -1293,7 +1293,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "9287", "itemId": "9287",
@ -1301,7 +1301,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "9288", "itemId": "9288",
@ -1309,7 +1309,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "9289", "itemId": "9289",
@ -1317,7 +1317,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "9290", "itemId": "9290",
@ -1325,7 +1325,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "9291", "itemId": "9291",
@ -1333,7 +1333,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "9293", "itemId": "9293",
@ -1341,7 +1341,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "9294", "itemId": "9294",
@ -1349,7 +1349,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "9295", "itemId": "9295",
@ -1357,7 +1357,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "9296", "itemId": "9296",
@ -1365,7 +1365,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "9297", "itemId": "9297",
@ -1373,7 +1373,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "9298", "itemId": "9298",
@ -1381,7 +1381,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "9300", "itemId": "9300",
@ -1389,7 +1389,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "9301", "itemId": "9301",
@ -1397,7 +1397,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "9302", "itemId": "9302",
@ -1405,7 +1405,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "9303", "itemId": "9303",
@ -1413,7 +1413,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "9304", "itemId": "9304",
@ -1421,7 +1421,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "9305", "itemId": "9305",
@ -1429,7 +1429,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "9335", "itemId": "9335",
@ -1557,7 +1557,7 @@
"start_graphic": "1116,96", "start_graphic": "1116,96",
"darkbow_graphic": "1114,96", "darkbow_graphic": "1114,96",
"projectile": "1120,40,36,41,46,5,0", "projectile": "1120,40,36,41,46,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "11228", "itemId": "11228",
@ -1565,7 +1565,7 @@
"start_graphic": "1116,96", "start_graphic": "1116,96",
"darkbow_graphic": "1114,96", "darkbow_graphic": "1114,96",
"projectile": "1120,40,36,41,46,5,0", "projectile": "1120,40,36,41,46,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "11229", "itemId": "11229",
@ -1573,7 +1573,7 @@
"start_graphic": "1116,96", "start_graphic": "1116,96",
"darkbow_graphic": "1114,96", "darkbow_graphic": "1114,96",
"projectile": "1120,40,36,41,46,5,0", "projectile": "1120,40,36,41,46,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "11230", "itemId": "11230",
@ -1589,7 +1589,7 @@
"start_graphic": "1123,96", "start_graphic": "1123,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1122,40,36,32,32,15,0", "projectile": "1122,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "11233", "itemId": "11233",
@ -1597,7 +1597,7 @@
"start_graphic": "1123,96", "start_graphic": "1123,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1122,40,36,32,32,15,0", "projectile": "1122,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "11234", "itemId": "11234",
@ -1605,7 +1605,7 @@
"start_graphic": "1123,96", "start_graphic": "1123,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1122,40,36,32,32,15,0", "projectile": "1122,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "13083", "itemId": "13083",
@ -1621,7 +1621,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "13085", "itemId": "13085",
@ -1629,7 +1629,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "13086", "itemId": "13086",
@ -1637,7 +1637,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "27,38,36,41,32,5,0", "projectile": "27,38,36,41,32,5,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "13280", "itemId": "13280",
@ -1661,7 +1661,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1837,40,36,32,32,15,0", "projectile": "1837,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "13881", "itemId": "13881",
@ -1669,7 +1669,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1837,40,36,32,32,15,0", "projectile": "1837,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "13882", "itemId": "13882",
@ -1677,7 +1677,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1837,40,36,32,32,15,0", "projectile": "1837,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "13883", "itemId": "13883",
@ -1701,7 +1701,7 @@
"start_graphic": "-1,0", "start_graphic": "-1,0",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1837,40,36,32,32,15,0", "projectile": "1837,40,36,32,32,15,0",
"poison_damage": "28" "poison_damage": "10"
}, },
{ {
"itemId": "13955", "itemId": "13955",
@ -1709,7 +1709,7 @@
"start_graphic": "1837,96", "start_graphic": "1837,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1840,40,36,32,32,15,0", "projectile": "1840,40,36,32,32,15,0",
"poison_damage": "38" "poison_damage": "15"
}, },
{ {
"itemId": "13956", "itemId": "13956",
@ -1717,7 +1717,7 @@
"start_graphic": "1837,96", "start_graphic": "1837,96",
"darkbow_graphic": "", "darkbow_graphic": "",
"projectile": "1840,40,36,32,32,15,0", "projectile": "1840,40,36,32,32,15,0",
"poison_damage": "48" "poison_damage": "20"
}, },
{ {
"itemId": "13957", "itemId": "13957",

View file

@ -36458,12 +36458,6 @@
"weight": "100.0", "weight": "100.0",
"id": "532", "id": "532",
"maxAmount": "1" "maxAmount": "1"
},
{
"minAmount": "3",
"weight": "100.0",
"id": "526",
"maxAmount": "3"
} }
], ],
"charm": [], "charm": [],

View file

@ -207,6 +207,10 @@
"item_id": "590", "item_id": "590",
"loc_data": "{1,2368,3135,0,140}-{1,2431,3072,0,140}-{1,3112,3369,2,140}-{1,3209,3734,0,100}-" "loc_data": "{1,2368,3135,0,140}-{1,2431,3072,0,140}-{1,3112,3369,2,140}-{1,3209,3734,0,100}-"
}, },
{
"item_id": "600",
"loc_data": "{1,2438,3187,0,200}-"
},
{ {
"item_id": "677", "item_id": "677",
"loc_data": "{1,3369,3378,0,150}-" "loc_data": "{1,3369,3378,0,150}-"
@ -233,7 +237,7 @@
}, },
{ {
"item_id": "946", "item_id": "946",
"loc_data": "{1,2903,3148,0,80}-{1,3205,3212,0,80}-{1,3224,3202,0,80}-{1,3218,3416,1,90}-{1,2820,3450,0,90}-{1,3106,3956,0,60}-{1,2566,9526,0,30}-{1,3215,9625,0,80}-{1,3218,9887,0,33}-{1,2700,3407,0,100}-" "loc_data": "{1,2903,3148,0,80}-{1,3205,3212,0,80}-{1,3224,3202,0,80}-{1,3218,3416,1,90}-{1,2820,3450,0,90}-{1,2700,3407,0,100}-{1,3106,3956,0,60}-{1,2566,9526,0,30}-{1,3215,9625,0,80}-{1,3218,9887,0,33}-"
}, },
{ {
"item_id": "952", "item_id": "952",
@ -605,7 +609,7 @@
}, },
{ {
"item_id": "3138", "item_id": "3138",
"loc_data": "{1,3463,9478,2,45}-{1,3461,9480,2,45}-{1,3461,9482,2,45}-{1,3461,9484,2,45}-" "loc_data": "{1,3461,9480,2,30}-{1,3460,9484,2,30}-{1,3465,9477,2,30}-{1,3467,9493,0,30}-{1,3486,9517,0,30}-{1,3474,9509,0,30}-{1,3470,9502,0,30}-{1,3480,9483,0,30}-"
}, },
{ {
"item_id": "3711", "item_id": "3711",
@ -671,6 +675,10 @@
"item_id": "11065", "item_id": "11065",
"loc_data": "{1,2928,3289,0,90}-" "loc_data": "{1,2928,3289,0,90}-"
}, },
{
"item_id": "11656",
"loc_data": "{1,2438,3185,0,200}-"
},
{ {
"item_id": "12494", "item_id": "12494",
"loc_data": "{1,2762,2973,0,60}-" "loc_data": "{1,2762,2973,0,60}-"

View file

@ -5575,7 +5575,7 @@
"id": "601" "id": "601"
}, },
{ {
"destroy_message": "You'll have to get another down in the Dungeon", "destroy_message": "You'll have to get another down in the dungeon.",
"examine": "An unusual clay mould in the shape of a disc.", "examine": "An unusual clay mould in the shape of a disc.",
"durability": null, "durability": null,
"name": "Lens mould", "name": "Lens mould",
@ -51573,6 +51573,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "483", "grand_exchange_price": "483",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Iron dagger(p+)", "name": "Iron dagger(p+)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51601,6 +51602,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "350", "grand_exchange_price": "350",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Bronze dagger(p+)", "name": "Bronze dagger(p+)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51630,6 +51632,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "575", "grand_exchange_price": "575",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Steel dagger(p+)", "name": "Steel dagger(p+)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51659,6 +51662,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "538", "grand_exchange_price": "538",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Mithril dagger(p+)", "name": "Mithril dagger(p+)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51684,6 +51688,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "800", "grand_exchange_price": "800",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Adamant dagger(p+)", "name": "Adamant dagger(p+)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51711,6 +51716,7 @@
"equip_audio": "2248", "equip_audio": "2248",
"render_anim": "2584", "render_anim": "2584",
"equipment_slot": "3", "equipment_slot": "3",
"attack_anims": "400,400,401,400",
"lendable": "true", "lendable": "true",
"grand_exchange_price": "4538", "grand_exchange_price": "4538",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
@ -51779,6 +51785,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "664", "grand_exchange_price": "664",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Black dagger(p+)", "name": "Black dagger(p+)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51826,6 +51833,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "4128", "grand_exchange_price": "4128",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Iron dagger(p++)", "name": "Iron dagger(p++)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51854,6 +51862,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "5088", "grand_exchange_price": "5088",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Br'ze dagger(p++)", "name": "Br'ze dagger(p++)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51883,6 +51892,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "4729", "grand_exchange_price": "4729",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Steel dagger(p++)", "name": "Steel dagger(p++)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51912,6 +51922,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "3476", "grand_exchange_price": "3476",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Mithril dagger(p++)", "name": "Mithril dagger(p++)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51937,6 +51948,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "4612", "grand_exchange_price": "4612",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Adamant dagger(p++)", "name": "Adamant dagger(p++)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -51967,6 +51979,7 @@
"lendable": "true", "lendable": "true",
"grand_exchange_price": "5061", "grand_exchange_price": "5061",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Rune dagger(p++)", "name": "Rune dagger(p++)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -52032,6 +52045,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "4921", "grand_exchange_price": "4921",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "Black dagger(p++)", "name": "Black dagger(p++)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -60306,6 +60320,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "497", "grand_exchange_price": "497",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "White dagger(p)", "name": "White dagger(p)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -60335,6 +60350,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "982", "grand_exchange_price": "982",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "White dagger(p+)", "name": "White dagger(p+)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",
@ -60364,6 +60380,7 @@
"equipment_slot": "3", "equipment_slot": "3",
"grand_exchange_price": "8422", "grand_exchange_price": "8422",
"attack_audios": "2517,2517,2500,2517", "attack_audios": "2517,2517,2500,2517",
"attack_anims": "400,400,401,400",
"name": "White dagger(p++)", "name": "White dagger(p++)",
"tradeable": "true", "tradeable": "true",
"archery_ticket_price": "0", "archery_ticket_price": "0",

View file

@ -2006,7 +2006,7 @@
"defence_animation": "6255", "defence_animation": "6255",
"weakness": "0", "weakness": "0",
"slayer_exp": "23", "slayer_exp": "23",
"poison_amount": "3", "poison_amount": "15",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "6256", "death_animation": "6256",
"name": "Poison Scorpion", "name": "Poison Scorpion",
@ -2517,7 +2517,7 @@
"respawn_delay": "30", "respawn_delay": "30",
"defence_animation": "5328", "defence_animation": "5328",
"weakness": "2", "weakness": "2",
"poison_amount": "6", "poison_amount": "30",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "5329", "death_animation": "5329",
"name": "Poison spider", "name": "Poison spider",
@ -3330,7 +3330,7 @@
"respawn_delay": "50", "respawn_delay": "50",
"defence_animation": "0", "defence_animation": "0",
"weakness": "8", "weakness": "8",
"poison_amount": "11", "poison_amount": "55",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "836", "death_animation": "836",
"name": "Tribesman", "name": "Tribesman",
@ -6227,6 +6227,22 @@
"range_level": "1", "range_level": "1",
"attack_level": "1" "attack_level": "1"
}, },
{
"examine": "A man, learned in the ways of the stars.",
"melee_animation": "0",
"range_animation": "0",
"defence_animation": "0",
"magic_animation": "0",
"death_animation": "0",
"name": "Observatory professor",
"defence_level": "1",
"safespot": null,
"lifepoints": "10",
"strength_level": "1",
"id": "488",
"range_level": "1",
"attack_level": "1"
},
{ {
"examine": "He doesn't look like he'd trust his own mother.", "examine": "He doesn't look like he'd trust his own mother.",
"melee_animation": "6199", "melee_animation": "6199",
@ -6237,13 +6253,13 @@
"magic_animation": "0", "magic_animation": "0",
"death_animation": "6190", "death_animation": "6190",
"name": "Goblin guard", "name": "Goblin guard",
"defence_level": "20", "defence_level": "37",
"safespot": null, "safespot": null,
"lifepoints": "28", "lifepoints": "43",
"strength_level": "20", "strength_level": "37",
"id": "489", "id": "489",
"range_level": "1", "range_level": "1",
"attack_level": "20" "attack_level": "32"
}, },
{ {
"examine": "If the mummy is at school", "examine": "If the mummy is at school",
@ -9414,7 +9430,7 @@
"attack_level": "15" "attack_level": "15"
}, },
{ {
"examine": "Children are just like real people...just smaller.", "examine": "A sad looking child.",
"melee_animation": "0", "melee_animation": "0",
"range_animation": "0", "range_animation": "0",
"defence_animation": "0", "defence_animation": "0",
@ -10513,18 +10529,18 @@
"respawn_delay": "30", "respawn_delay": "30",
"defence_animation": "5328", "defence_animation": "5328",
"weakness": "2", "weakness": "2",
"poison_amount": "6", "poison_amount": "30",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "5329", "death_animation": "5329",
"name": "Poison spider", "name": "Poison spider",
"safespot": null, "safespot": null,
"defence_level": "52", "defence_level": "28",
"lifepoints": "64", "lifepoints": "25",
"strength_level": "65", "strength_level": "28",
"id": "1009", "id": "1009",
"aggressive": "true", "aggressive": "true",
"range_level": "1", "range_level": "1",
"attack_level": "50" "attack_level": "28"
}, },
{ {
"examine": "A green skinned croaker", "examine": "A green skinned croaker",
@ -12353,7 +12369,7 @@
"defence_animation": "6227", "defence_animation": "6227",
"weakness": "7", "weakness": "7",
"slayer_exp": "90", "slayer_exp": "90",
"poison_amount": "4", "poison_amount": "20",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "6228", "death_animation": "6228",
"name": "Kalphite Soldier", "name": "Kalphite Soldier",
@ -12377,7 +12393,7 @@
"respawn_delay": "65", "respawn_delay": "65",
"defence_animation": "6232", "defence_animation": "6232",
"weakness": "7", "weakness": "7",
"poison_amount": "6", "poison_amount": "30",
"magic_animation": "6223", "magic_animation": "6223",
"death_animation": "6230", "death_animation": "6230",
"name": "Kalphite Guardian", "name": "Kalphite Guardian",
@ -12424,7 +12440,7 @@
"respawn_delay": "65", "respawn_delay": "65",
"defence_animation": "6232", "defence_animation": "6232",
"weakness": "7", "weakness": "7",
"poison_amount": "6", "poison_amount": "30",
"magic_animation": "6223", "magic_animation": "6223",
"death_animation": "6230", "death_animation": "6230",
"name": "Kalphite Guardian", "name": "Kalphite Guardian",
@ -12444,7 +12460,7 @@
"melee_animation": "6241", "melee_animation": "6241",
"range_animation": "6241", "range_animation": "6241",
"attack_speed": "4", "attack_speed": "4",
"poisonous": "true", "poisonous": "",
"magic_level": "150", "magic_level": "150",
"respawn_delay": "0", "respawn_delay": "0",
"defence_animation": "6232", "defence_animation": "6232",
@ -13455,6 +13471,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "0",
"weakness": "3", "weakness": "3",
"poison_amount": "30",
"magic_animation": "811", "magic_animation": "811",
"death_animation": "836", "death_animation": "836",
"name": "Saradomin wizard", "name": "Saradomin wizard",
@ -16454,6 +16471,7 @@
"defence_animation": "267", "defence_animation": "267",
"weakness": "1", "weakness": "1",
"slayer_exp": "22", "slayer_exp": "22",
"poison_amount": "40",
"magic_animation": "266", "magic_animation": "266",
"death_animation": "265", "death_animation": "265",
"name": "Cave crawler", "name": "Cave crawler",
@ -16476,6 +16494,7 @@
"respawn_delay": "25", "respawn_delay": "25",
"defence_animation": "267", "defence_animation": "267",
"slayer_exp": "22", "slayer_exp": "22",
"poison_amount": "40",
"magic_animation": "266", "magic_animation": "266",
"death_animation": "265", "death_animation": "265",
"name": "Cave crawler", "name": "Cave crawler",
@ -16498,6 +16517,7 @@
"respawn_delay": "25", "respawn_delay": "25",
"defence_animation": "267", "defence_animation": "267",
"slayer_exp": "22", "slayer_exp": "22",
"poison_amount": "40",
"magic_animation": "266", "magic_animation": "266",
"death_animation": "265", "death_animation": "265",
"name": "Cave crawler", "name": "Cave crawler",
@ -16520,6 +16540,7 @@
"respawn_delay": "25", "respawn_delay": "25",
"defence_animation": "267", "defence_animation": "267",
"slayer_exp": "22", "slayer_exp": "22",
"poison_amount": "40",
"magic_animation": "266", "magic_animation": "266",
"death_animation": "265", "death_animation": "265",
"name": "Cave crawler", "name": "Cave crawler",
@ -19313,7 +19334,7 @@
"defence_animation": "0", "defence_animation": "0",
"weakness": "1", "weakness": "1",
"slayer_exp": "25", "slayer_exp": "25",
"poison_amount": "3", "poison_amount": "15",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "1792", "death_animation": "1792",
"name": "Cave slime", "name": "Cave slime",
@ -20627,6 +20648,7 @@
"poisonous": "true", "poisonous": "true",
"defence_animation": "1946", "defence_animation": "1946",
"weakness": "7", "weakness": "7",
"poison_amount": "15",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "5464", "death_animation": "5464",
"name": "Scarabs", "name": "Scarabs",
@ -20964,6 +20986,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "0",
"weakness": "6", "weakness": "6",
"poison_amount": "15",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "1946", "death_animation": "1946",
"name": "Scarab swarm", "name": "Scarab swarm",
@ -25076,7 +25099,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "0",
"weakness": "1", "weakness": "1",
"poison_amount": "11", "poison_amount": "55",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "278", "death_animation": "278",
"name": "Bush snake", "name": "Bush snake",
@ -25095,7 +25118,7 @@
"poisonous": "true", "poisonous": "true",
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "276", "defence_animation": "276",
"poison_amount": "11", "poison_amount": "55",
"death_animation": "278", "death_animation": "278",
"name": "Bush snake", "name": "Bush snake",
"defence_level": "1", "defence_level": "1",
@ -25211,7 +25234,7 @@
"respawn_delay": "50", "respawn_delay": "50",
"defence_animation": "0", "defence_animation": "0",
"weakness": "9", "weakness": "9",
"poison_amount": "11", "poison_amount": "55",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "836", "death_animation": "836",
"name": "Tribesman", "name": "Tribesman",
@ -29571,6 +29594,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "2937", "defence_animation": "2937",
"weakness": "9", "weakness": "9",
"poison_amount": "40",
"magic_animation": "2936", "magic_animation": "2936",
"death_animation": "2938", "death_animation": "2938",
"name": "Jogre Champion", "name": "Jogre Champion",
@ -29609,7 +29633,7 @@
"examine": "Champion of the jogres.", "examine": "Champion of the jogres.",
"melee_animation": "5485", "melee_animation": "5485",
"range_animation": "5493", "range_animation": "5493",
"poisonous": "true", "poisonous": "",
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "5493", "defence_animation": "5493",
"weakness": "9", "weakness": "9",
@ -32631,6 +32655,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "0",
"weakness": "10", "weakness": "10",
"poison_amount": "35",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "7249", "death_animation": "7249",
"name": "Wild jade vine", "name": "Wild jade vine",
@ -32652,6 +32677,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "0",
"weakness": "10", "weakness": "10",
"poison_amount": "35",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "7249", "death_animation": "7249",
"name": "Wild jade vine", "name": "Wild jade vine",
@ -32673,6 +32699,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "0",
"weakness": "10", "weakness": "10",
"poison_amount": "35",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "7249", "death_animation": "7249",
"name": "Wild jade vine", "name": "Wild jade vine",
@ -32694,6 +32721,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "0",
"weakness": "10", "weakness": "10",
"poison_amount": "35",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "7249", "death_animation": "7249",
"name": "Wild jade vine", "name": "Wild jade vine",
@ -33773,7 +33801,7 @@
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "6227", "defence_animation": "6227",
"weakness": "6", "weakness": "6",
"poison_amount": "4", "poison_amount": "20",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "6228", "death_animation": "6228",
"name": "Kalphite Soldier", "name": "Kalphite Soldier",
@ -35991,7 +36019,7 @@
"name": "Tortoise", "name": "Tortoise",
"defence_level": "36", "defence_level": "36",
"safespot": null, "safespot": null,
"lifepoints": "51", "lifepoints": "121",
"strength_level": "36", "strength_level": "36",
"id": "3808", "id": "3808",
"range_level": "1", "range_level": "1",
@ -36065,61 +36093,68 @@
"examine": "A Gnome Arrow-chucker", "examine": "A Gnome Arrow-chucker",
"combat_style": "1", "combat_style": "1",
"melee_animation": "190", "melee_animation": "190",
"range_animation": "0", "range_animation": "190",
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "193",
"weakness": "2", "weakness": "2",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "196", "death_animation": "196",
"name": "Gnome Archer", "name": "Gnome Archer",
"defence_level": "30", "defence_level": "1",
"safespot": null, "safespot": null,
"lifepoints": "42", "lifepoints": "10",
"strength_level": "1", "strength_level": "1",
"id": "3814", "id": "3814",
"aggressive": "true", "aggressive": "true",
"range_level": "30", "range_level": "5",
"attack_level": "1" "projectile": "10",
"attack_level": "1",
"prj_height": "30"
}, },
{ {
"examine": "Yee haa!", "examine": "Yee haa!",
"melee_animation": "3969", "melee_animation": "3969",
"range_animation": "0", "range_animation": "0",
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "193",
"weakness": "9", "weakness": "9",
"magic_animation": "0", "magic_animation": "0",
"death_animation": "196", "death_animation": "196",
"name": "Gnome Driver", "name": "Gnome Driver",
"defence_level": "33", "defence_level": "1",
"safespot": null, "safespot": null,
"lifepoints": "47", "lifepoints": "10",
"strength_level": "33", "strength_level": "1",
"id": "3815", "id": "3815",
"aggressive": "true", "aggressive": "true",
"range_level": "1", "range_level": "1",
"attack_level": "33" "attack_level": "5"
}, },
{ {
"examine": "A battle mage of the gnomish variety.", "examine": "A battle mage of the gnomish variety.",
"combat_style": "2", "combat_style": "2",
"start_gfx": "93",
"start_height": "80",
"melee_animation": "3968", "melee_animation": "3968",
"range_animation": "0", "range_animation": "0",
"magic_level": "34", "magic_level": "5",
"spell_id": "",
"respawn_delay": "60", "respawn_delay": "60",
"defence_animation": "0", "defence_animation": "193",
"weakness": "5", "weakness": "5",
"magic_animation": "0", "magic_animation": "200",
"death_animation": "196", "death_animation": "196",
"name": "Gnome Mage", "name": "Gnome Mage",
"defence_level": "34", "defence_level": "1",
"safespot": null, "safespot": null,
"lifepoints": "48", "lifepoints": "10",
"strength_level": "1", "strength_level": "1",
"id": "3816", "id": "3816",
"aggressive": "true", "aggressive": "true",
"range_level": "1", "range_level": "1",
"attack_level": "1" "projectile": "94",
"attack_level": "1",
"prj_height": "30"
}, },
{ {
"examine": "The cruel tortoise trainer. Boo!", "examine": "The cruel tortoise trainer. Boo!",
@ -36148,7 +36183,7 @@
"name": "Tortoise", "name": "Tortoise",
"defence_level": "36", "defence_level": "36",
"safespot": null, "safespot": null,
"lifepoints": "51", "lifepoints": "101",
"strength_level": "36", "strength_level": "36",
"id": "3819", "id": "3819",
"range_level": "1", "range_level": "1",
@ -53243,6 +53278,7 @@
"attack_level": "1" "attack_level": "1"
}, },
{ {
"examine": "An ugly green creature.",
"name": "Goblin", "name": "Goblin",
"defence_level": "1", "defence_level": "1",
"safespot": null, "safespot": null,
@ -53840,11 +53876,11 @@
"examine": "A servant of the god Zamorak. ", "examine": "A servant of the god Zamorak. ",
"melee_animation": "6945", "melee_animation": "6945",
"attack_speed": "6", "attack_speed": "6",
"poisonous": "true", "poisonous": "",
"respawn_delay": "150", "respawn_delay": "150",
"weakness": "9", "weakness": "9",
"slayer_exp": "350", "slayer_exp": "350",
"poison_amount": "16", "poison_amount": "",
"magic_animation": "6945", "magic_animation": "6945",
"death_animation": "6946", "death_animation": "6946",
"lifepoints": "255", "lifepoints": "255",
@ -54943,7 +54979,7 @@
"combat_style": "2", "combat_style": "2",
"melee_animation": "811", "melee_animation": "811",
"range_animation": "811", "range_animation": "811",
"poisonous": "true", "poisonous": "",
"magic_level": "60", "magic_level": "60",
"respawn_delay": "25", "respawn_delay": "25",
"end_gfx": "76", "end_gfx": "76",
@ -58366,11 +58402,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -58522,11 +58558,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -58681,11 +58717,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -58742,11 +58778,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -58822,11 +58858,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -59145,11 +59181,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -59225,11 +59261,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -59427,6 +59463,7 @@
"examine": "A ghost of an ork slain during the god wars.", "examine": "A ghost of an ork slain during the god wars.",
"melee_animation": "7411", "melee_animation": "7411",
"range_animation": "7518", "range_animation": "7518",
"poisonous": "",
"magic_level": "70", "magic_level": "70",
"defence_animation": "7413", "defence_animation": "7413",
"magic_animation": "7505", "magic_animation": "7505",
@ -59446,11 +59483,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -59605,11 +59642,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -59620,7 +59657,8 @@
"id": "6673", "id": "6673",
"clue_level": "2", "clue_level": "2",
"range_level": "80", "range_level": "80",
"attack_level": "80" "attack_level": "80",
"prj_height": ""
}, },
{ {
"spawn_animation": "7426", "spawn_animation": "7426",
@ -59666,11 +59704,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -59989,11 +60027,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -60426,11 +60464,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -60667,11 +60705,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -60746,11 +60784,11 @@
"examine": "A ghost of a knight slain during the god wars.", "examine": "A ghost of a knight slain during the god wars.",
"melee_animation": "7441", "melee_animation": "7441",
"range_animation": "7522", "range_animation": "7522",
"poisonous": "true", "poisonous": "",
"magic_level": "80", "magic_level": "80",
"defence_animation": "7443", "defence_animation": "7443",
"slayer_exp": "143", "slayer_exp": "143",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "7508", "magic_animation": "7508",
"death_animation": "7442", "death_animation": "7442",
"name": "Revenant knight", "name": "Revenant knight",
@ -63810,9 +63848,9 @@
"melee_animation": "8591", "melee_animation": "8591",
"range_animation": "8594", "range_animation": "8594",
"combat_audio": "408,410,409", "combat_audio": "408,410,409",
"poisonous": "true", "poisonous": "",
"defence_animation": "8592", "defence_animation": "8592",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "8594", "magic_animation": "8594",
"death_animation": "8593", "death_animation": "8593",
"name": "Revenant dragon", "name": "Revenant dragon",
@ -63831,9 +63869,9 @@
"melee_animation": "8591", "melee_animation": "8591",
"range_animation": "8594", "range_animation": "8594",
"combat_audio": "408,410,409", "combat_audio": "408,410,409",
"poisonous": "true", "poisonous": "",
"defence_animation": "8592", "defence_animation": "8592",
"poison_amount": "8", "poison_amount": "",
"magic_animation": "8594", "magic_animation": "8594",
"death_animation": "8593", "death_animation": "8593",
"name": "Revenant dragon", "name": "Revenant dragon",
@ -64576,6 +64614,7 @@
"death_animation": "8756", "death_animation": "8756",
"name": "Bork", "name": "Bork",
"defence_level": "80", "defence_level": "80",
"poison_immune": "true",
"lifepoints": "300", "lifepoints": "300",
"strength_level": "90", "strength_level": "90",
"id": "7133", "id": "7133",
@ -64693,6 +64732,7 @@
"name": "Bork", "name": "Bork",
"defence_level": "1", "defence_level": "1",
"safespot": null, "safespot": null,
"poison_immune": "true",
"lifepoints": "300", "lifepoints": "300",
"strength_level": "1", "strength_level": "1",
"id": "7134", "id": "7134",
@ -73499,10 +73539,6 @@
"name": "Kalron", "name": "Kalron",
"id": "486" "id": "486"
}, },
{
"name": "Observatory professor",
"id": "488"
},
{ {
"name": "Hajedy", "name": "Hajedy",
"id": "510" "id": "510"
@ -74736,6 +74772,7 @@
}, },
{ {
"name": "Solihib", "name": "Solihib",
"movement_radius": "1",
"id": "1433" "id": "1433"
}, },
{ {
@ -82634,6 +82671,7 @@
"id": "6114" "id": "6114"
}, },
{ {
"examine": "A man, learned in the ways of the stars.",
"name": "Observatory professor", "name": "Observatory professor",
"id": "6119" "id": "6119"
}, },

View file

@ -157,7 +157,7 @@
}, },
{ {
"npc_id": "47", "npc_id": "47",
"loc_data": "{2821,3170,0,1,1}-{3341,3267,0,1,5}-{3076,3282,0,1,5}-{3089,3266,0,1,4}-{3091,3266,0,1,4}-{3097,3364,0,1,3}-{3102,3363,0,1,5}-{3127,3487,0,1,4}-{3125,3486,0,1,6}-{3127,3486,0,1,4}-{2603,9480,0,1,1}-{2600,9477,0,1,0}-{2579,9496,0,1,4}-{2580,9508,0,1,0}-{2571,9522,0,1,4}-{2565,9505,0,1,1}-{2566,9510,0,1,6}-{2594,9497,0,1,4}-{2852,9642,0,1,6}-{2858,9632,0,1,3}-{2568,9620,0,1,0}-{2573,9612,0,1,0}-{2579,9631,0,1,0}-{2580,9600,0,1,0}-{2580,9614,0,1,0}-{2580,9620,0,1,0}-{2580,9626,0,1,0}-{2583,9632,0,1,0}-{2584,9625,0,1,0}-{2584,9637,0,1,0}-{2589,9644,0,1,0}-{2590,9601,0,1,0}-{2590,9638,0,1,0}-{2591,9601,0,1,0}-{2591,9621,0,1,0}-{2594,9636,0,1,0}-{2594,9644,0,1,0}-{2597,9604,0,1,0}-{2607,9615,0,1,0}-{2608,9628,0,1,0}-{2614,9651,0,1,0}-{2614,9656,0,1,0}-{2615,9647,0,1,0}-{2615,9661,0,1,0}-{2616,9633,0,1,0}-{2618,9630,0,1,0}-{3108,9754,0,1,5}-{3110,9754,0,1,5}-{3108,9750,0,1,5}-{2592,9831,0,1,3}-{2588,9825,0,1,6}-{2583,9829,0,1,4}-{2581,9841,0,1,0}-{2597,9823,0,1,2}-{2579,9805,0,1,3}-{2576,9804,0,1,0}-{2573,9805,0,1,5}-{2571,9808,0,1,3}-{2576,9810,0,1,2}-{2587,9802,0,1,2}-{2592,9800,0,1,4}-{2596,9805,0,1,6}-{2601,9802,0,1,5}-{2585,9801,0,1,7}-{2594,9803,0,1,0}-{2590,9806,0,1,3}-{2612,9808,0,1,6}-{2604,9810,0,1,6}-{2579,9821,0,1,2}-{2576,9812,0,1,6}-{2580,9813,0,1,6}-{2600,9813,0,1,4}-{2599,9809,0,1,4}-{3158,3226,0,1,5}-{3160,3202,0,1,4}-{3192,3203,0,1,0}-{3194,3204,0,1,0}-{3196,3206,0,1,0}-{3197,3204,0,1,0}-{2654,9640,0,1,6}-{2655,9637,0,1,4}-{2656,9639,0,1,7}-{2651,9636,0,1,5}-{2648,9637,0,1,4}-{2651,9642,0,1,1}-{2654,9640,0,1,0}-{2654,9635,0,1,6}-{2655,9635,0,1,3}-{2664,9626,0,1,6}-{2664,9624,0,1,1}-{2661,9623,0,1,1}-{2663,9623,0,1,3}-{2664,9626,0,1,6}-{2930,9699,0,1,0}-{2933,9697,0,1,0}-{2932,9685,0,1,0}-{2930,9693,0,1,0}-{3235,3224,0,1,3}-{3229,3220,0,1,4}-{3211,3211,0,1,3}-{3225,3220,0,1,1}-{3237,3215,0,1,5}-{3211,3210,0,1,7}-{3227,3220,0,1,7}-{3233,3227,0,1,5}-{3227,3210,0,1,6}-{3228,3222,0,1,4}-{3229,3226,0,1,0}-{3236,3217,0,1,4}-{3259,3230,0,1,4}-{3233,3237,0,1,7}-{3205,3204,0,1,0}-{3206,3204,0,1,0}-{3205,3203,0,1,0}-{3206,3202,0,1,0}-{3207,3202,0,1,0}-{3208,3203,0,1,0}-{3001,3202,0,1,5}-{3243,3687,0,1,5}-{3249,3669,0,1,3}-{3252,3675,0,1,4}-{3252,3680,0,1,3}-{3259,3683,0,1,0}-{3475,9840,0,1,6}-{3481,9842,0,1,1}-{3486,9843,0,1,7}-{3483,9824,0,1,4}-{3496,9808,0,0,5}-{3490,9815,0,1,1}-{3478,9834,0,0,3}-{3490,9824,0,1,4}-{3225,9862,0,1,4}-{3222,9861,0,1,6}-{3220,9860,0,1,6}-{3219,9865,0,1,6}-{3237,9862,0,1,4}-{2536,2982,0,1,3}-{2531,2980,0,1,0}-{2522,2981,0,1,4}-{2545,2989,0,1,4}-{2523,2970,0,1,2}-{3026,3174,0,1,5}-{3019,3176,0,1,7}-{2801,3158,0,1,2}-{2514,3193,0,1,6}-{2518,3192,0,1,3}-{2507,3181,0,1,3}-{2508,3178,0,1,6}-{2511,3183,0,1,3}-{2515,3182,0,1,1}-{3021,3205,0,1,6}-{3019,3292,0,1,7}-{3018,3295,0,1,7}-{2531,3325,0,1,3}-{2530,3327,0,1,5}-{2521,3331,0,1,3}-{2526,3328,0,1,3}-{2523,3331,0,1,4}-{2523,3334,0,1,1}-{2531,3329,0,1,5}-{2532,3333,0,1,5}-{3276,9871,0,1,1}-{3277,9871,0,1,3}-" "loc_data": "{2821,3170,0,1,1}-{3341,3267,0,1,5}-{3076,3282,0,1,5}-{3089,3266,0,1,4}-{3091,3266,0,1,4}-{3097,3364,0,1,3}-{3102,3363,0,1,5}-{3127,3487,0,1,4}-{3125,3486,0,1,6}-{3127,3486,0,1,4}-{2339,9356,0,1,0}-{2354,9390,0,1,0}-{2361,9403,0,1,0}-{2362,9347,0,1,0}-{2603,9480,0,1,1}-{2600,9477,0,1,0}-{2579,9496,0,1,4}-{2580,9508,0,1,0}-{2571,9522,0,1,4}-{2565,9505,0,1,1}-{2566,9510,0,1,6}-{2594,9497,0,1,4}-{2852,9642,0,1,6}-{2858,9632,0,1,3}-{2568,9620,0,1,0}-{2573,9612,0,1,0}-{2579,9631,0,1,0}-{2580,9600,0,1,0}-{2580,9614,0,1,0}-{2580,9620,0,1,0}-{2580,9626,0,1,0}-{2583,9632,0,1,0}-{2584,9625,0,1,0}-{2584,9637,0,1,0}-{2589,9644,0,1,0}-{2590,9601,0,1,0}-{2590,9638,0,1,0}-{2591,9601,0,1,0}-{2591,9621,0,1,0}-{2594,9636,0,1,0}-{2594,9644,0,1,0}-{2597,9604,0,1,0}-{2607,9615,0,1,0}-{2608,9628,0,1,0}-{2614,9651,0,1,0}-{2614,9656,0,1,0}-{2615,9647,0,1,0}-{2615,9661,0,1,0}-{2616,9633,0,1,0}-{2618,9630,0,1,0}-{3108,9754,0,1,5}-{3110,9754,0,1,5}-{3108,9750,0,1,5}-{2592,9831,0,1,3}-{2588,9825,0,1,6}-{2583,9829,0,1,4}-{2581,9841,0,1,0}-{2597,9823,0,1,2}-{2579,9805,0,1,3}-{2576,9804,0,1,0}-{2573,9805,0,1,5}-{2571,9808,0,1,3}-{2576,9810,0,1,2}-{2587,9802,0,1,2}-{2592,9800,0,1,4}-{2596,9805,0,1,6}-{2601,9802,0,1,5}-{2585,9801,0,1,7}-{2594,9803,0,1,0}-{2590,9806,0,1,3}-{2612,9808,0,1,6}-{2604,9810,0,1,6}-{2579,9821,0,1,2}-{2576,9812,0,1,6}-{2580,9813,0,1,6}-{2600,9813,0,1,4}-{2599,9809,0,1,4}-{3158,3226,0,1,5}-{3160,3202,0,1,4}-{3192,3203,0,1,0}-{3194,3204,0,1,0}-{3196,3206,0,1,0}-{3197,3204,0,1,0}-{2654,9640,0,1,6}-{2655,9637,0,1,4}-{2656,9639,0,1,7}-{2651,9636,0,1,5}-{2648,9637,0,1,4}-{2651,9642,0,1,1}-{2654,9640,0,1,0}-{2654,9635,0,1,6}-{2655,9635,0,1,3}-{2664,9626,0,1,6}-{2664,9624,0,1,1}-{2661,9623,0,1,1}-{2663,9623,0,1,3}-{2664,9626,0,1,6}-{2930,9699,0,1,0}-{2933,9697,0,1,0}-{2932,9685,0,1,0}-{2930,9693,0,1,0}-{3235,3224,0,1,3}-{3229,3220,0,1,4}-{3211,3211,0,1,3}-{3225,3220,0,1,1}-{3237,3215,0,1,5}-{3211,3210,0,1,7}-{3227,3220,0,1,7}-{3233,3227,0,1,5}-{3227,3210,0,1,6}-{3228,3222,0,1,4}-{3229,3226,0,1,0}-{3236,3217,0,1,4}-{3259,3230,0,1,4}-{3233,3237,0,1,7}-{3205,3204,0,1,0}-{3206,3204,0,1,0}-{3205,3203,0,1,0}-{3206,3202,0,1,0}-{3207,3202,0,1,0}-{3208,3203,0,1,0}-{3001,3202,0,1,5}-{3243,3687,0,1,5}-{3249,3669,0,1,3}-{3252,3675,0,1,4}-{3252,3680,0,1,3}-{3259,3683,0,1,0}-{3475,9840,0,1,6}-{3481,9842,0,1,1}-{3486,9843,0,1,7}-{3483,9824,0,1,4}-{3496,9808,0,0,5}-{3490,9815,0,1,1}-{3478,9834,0,0,3}-{3490,9824,0,1,4}-{3225,9862,0,1,4}-{3222,9861,0,1,6}-{3220,9860,0,1,6}-{3219,9865,0,1,6}-{3237,9862,0,1,4}-{2536,2982,0,1,3}-{2531,2980,0,1,0}-{2522,2981,0,1,4}-{2545,2989,0,1,4}-{2523,2970,0,1,2}-{3026,3174,0,1,5}-{3019,3176,0,1,7}-{2801,3158,0,1,2}-{2514,3193,0,1,6}-{2518,3192,0,1,3}-{2507,3181,0,1,3}-{2508,3178,0,1,6}-{2511,3183,0,1,3}-{2515,3182,0,1,1}-{3021,3205,0,1,6}-{3019,3292,0,1,7}-{3018,3295,0,1,7}-{2531,3325,0,1,3}-{2530,3327,0,1,5}-{2521,3331,0,1,3}-{2526,3328,0,1,3}-{2523,3331,0,1,4}-{2523,3334,0,1,1}-{2531,3329,0,1,5}-{2532,3333,0,1,5}-{3276,9871,0,1,1}-{3277,9871,0,1,3}-"
}, },
{ {
"npc_id": "48", "npc_id": "48",
@ -193,7 +193,7 @@
}, },
{ {
"npc_id": "59", "npc_id": "59",
"loc_data": "{3082,3362,0,1,4}-{2602,9640,0,1,0}-{2603,9635,0,1,0}-{2603,9638,0,1,0}-{2605,9637,0,1,0}-{2605,9639,0,1,0}-{2606,9635,0,1,0}-{2606,9646,0,1,0}-{2607,9637,0,1,0}-{2607,9641,0,1,0}-{2607,9644,0,1,0}-{2607,9648,0,1,0}-{2608,9635,0,1,0}-{2608,9642,0,1,0}-{2609,9639,0,1,0}-{2609,9643,0,1,0}-{3102,9881,0,1,3}-{3095,9883,0,1,2}-{3182,3244,0,1,1}-{3162,3223,0,1,7}-{3164,3242,0,1,0}-{3169,3246,0,1,0}-{3170,3250,0,1,0}-{3166,3247,0,1,0}-{3164,3249,0,1,0}-{3157,3226,0,1,0}-{3163,3227,0,1,0}-{3165,3223,0,1,0}-{3194,3236,0,1,0}-{3146,3347,0,1,3}-{2648,9766,0,1,4}-{2653,9761,0,1,3}-{2483,2877,0,1,3}-{2481,2876,0,1,4}-{2457,2867,0,1,4}-{2481,2847,0,1,0}-{2475,2854,0,1,3}-{2482,2873,0,1,3}-{2485,2876,0,1,2}-{2449,2865,0,1,1}-{2461,2880,0,1,5}-{2489,2935,0,1,7}-{2492,2907,0,1,1}-{2487,2894,0,1,2}-{2487,2888,0,1,7}-{2478,2916,0,1,5}-{2485,2902,0,1,3}-{2487,2902,0,1,2}-{2489,2894,0,1,2}-{2490,2905,0,1,7}-{2490,2917,0,1,5}-{2484,2890,0,1,1}-{2491,2927,0,1,1}-{3250,3239,0,1,1}-{3241,3241,0,1,1}-{3249,3249,0,1,1}-{3218,9890,0,1,3}-{3220,9887,0,1,0}-{3218,9887,0,1,1}-{3218,9889,0,1,5}-{3213,9890,0,1,1}-{2496,2890,0,1,4}-{2503,2889,0,1,1}-{2497,2939,0,1,2}-{2369,3374,0,1,0}-{2372,3379,0,1,0}-{2378,3366,0,1,0}-{2379,3378,0,1,0}-{2387,3370,0,1,0}-{2394,3365,0,1,0}-{2402,3386,0,1,0}-{2405,3381,0,1,0}-{2407,3387,0,1,0}-{2412,3384,0,1,0}-" "loc_data": "{3082,3362,0,1,4}-{2602,9640,0,1,0}-{2603,9635,0,1,0}-{2603,9638,0,1,0}-{2605,9637,0,1,0}-{2605,9639,0,1,0}-{2606,9635,0,1,0}-{2606,9646,0,1,0}-{2607,9637,0,1,0}-{2607,9641,0,1,0}-{2607,9644,0,1,0}-{2607,9648,0,1,0}-{2608,9635,0,1,0}-{2608,9642,0,1,0}-{2609,9639,0,1,0}-{2609,9643,0,1,0}-{3102,9881,0,1,3}-{3095,9883,0,1,2}-{3182,3244,0,1,1}-{3162,3223,0,1,7}-{3164,3242,0,1,0}-{3169,3246,0,1,0}-{3170,3250,0,1,0}-{3166,3247,0,1,0}-{3164,3249,0,1,0}-{3157,3226,0,1,0}-{3163,3227,0,1,0}-{3165,3223,0,1,0}-{3194,3236,0,1,0}-{3146,3347,0,1,3}-{2369,3374,0,1,0}-{2372,3379,0,1,0}-{2378,3366,0,1,0}-{2379,3378,0,1,0}-{2387,3370,0,1,0}-{2394,3365,0,1,0}-{2402,3386,0,1,0}-{2405,3381,0,1,0}-{2407,3387,0,1,0}-{2412,3384,0,1,0}-{2648,9766,0,1,4}-{2653,9761,0,1,3}-{2483,2877,0,1,3}-{2481,2876,0,1,4}-{2457,2867,0,1,4}-{2481,2847,0,1,0}-{2475,2854,0,1,3}-{2482,2873,0,1,3}-{2485,2876,0,1,2}-{2449,2865,0,1,1}-{2461,2880,0,1,5}-{2489,2935,0,1,7}-{2492,2907,0,1,1}-{2487,2894,0,1,2}-{2487,2888,0,1,7}-{2478,2916,0,1,5}-{2485,2902,0,1,3}-{2487,2902,0,1,2}-{2489,2894,0,1,2}-{2490,2905,0,1,7}-{2490,2917,0,1,5}-{2484,2890,0,1,1}-{2491,2927,0,1,1}-{3250,3239,0,1,1}-{3241,3241,0,1,1}-{3249,3249,0,1,1}-{3218,9890,0,1,3}-{3220,9887,0,1,0}-{3218,9887,0,1,1}-{3218,9889,0,1,5}-{3213,9890,0,1,1}-{2496,2890,0,1,4}-{2503,2889,0,1,1}-{2497,2939,0,1,2}-"
}, },
{ {
"npc_id": "60", "npc_id": "60",
@ -217,15 +217,15 @@
}, },
{ {
"npc_id": "66", "npc_id": "66",
"loc_data": "{2417,3493,1,1,0}-{2418,3495,1,1,0}-{2383,3452,0,1,0}-{2401,3417,0,1,0}-{2402,3422,0,1,0}-{2402,3441,0,1,0}-{2403,3430,0,1,0}-{2408,3441,0,1,0}-{2409,3430,0,1,0}-{2423,3426,0,1,0}-{2427,3428,0,1,0}-{2427,3440,0,1,0}-{2526,3168,0,1,0}-{2529,3163,0,1,0}-{2530,3172,0,1,0}-{2536,3169,0,1,0}-{2435,3460,0,1,0}-{2440,3470,0,1,0}-{2447,3502,0,1,0}-{2457,3462,0,1,0}-{2478,3502,0,1,0}-{2449,3492,1,1,0}-{2450,3490,1,1,0}-{2474,3490,1,1,0}-{2482,3492,1,1,0}-{2482,3498,1,1,0}-{2399,3356,0,1,0}-" "loc_data": "{2399,3356,0,1,0}-{2383,3452,0,1,0}-{2401,3417,0,1,0}-{2402,3422,0,1,0}-{2402,3441,0,1,0}-{2403,3430,0,1,0}-{2408,3441,0,1,0}-{2409,3430,0,1,0}-{2423,3426,0,1,0}-{2427,3428,0,1,0}-{2427,3440,0,1,0}-{2417,3493,1,1,0}-{2418,3495,1,1,0}-{2435,3460,0,1,0}-{2440,3470,0,1,0}-{2447,3502,0,1,0}-{2457,3462,0,1,0}-{2478,3502,0,1,0}-{2449,3492,1,1,0}-{2450,3490,1,1,0}-{2474,3490,1,1,0}-{2482,3492,1,1,0}-{2482,3498,1,1,0}-{2526,3168,0,1,0}-{2529,3163,0,1,0}-{2530,3172,0,1,0}-{2536,3169,0,1,0}-"
}, },
{ {
"npc_id": "67", "npc_id": "67",
"loc_data": "{2556,3226,0,1,0}-{2456,3425,0,1,0}-{2459,3421,0,1,0}-{2462,3431,0,1,0}-{2394,3500,1,1,0}-{2417,3483,1,1,0}-{2377,3442,0,1,0}-{2380,3425,0,1,0}-{2383,3433,0,1,0}-{2405,3447,0,1,0}-{2406,3440,0,1,0}-{2393,3451,1,1,0}-{2408,3437,1,1,0}-{2521,3169,0,1,0}-{2521,3171,0,1,0}-{2033,5530,1,1,0}-{2437,3478,0,1,0}-{2442,3465,0,1,0}-{2456,3467,0,1,0}-{2471,3496,0,1,0}-{2474,3508,0,1,0}-{2475,3471,0,1,0}-{2479,3468,0,1,0}-{2486,3470,0,1,0}-{2492,3474,0,1,0}-{2443,3464,1,1,0}-{2443,3502,1,1,0}-{2448,3496,1,1,0}-{2449,3506,1,1,0}-{2457,3498,1,1,0}-{2474,3498,1,1,0}-{2480,3502,1,1,0}-{2481,3482,1,1,0}-{2490,3503,1,1,0}-{2400,3356,0,1,0}-" "loc_data": "{2400,3356,0,1,0}-{2377,3442,0,1,0}-{2380,3425,0,1,0}-{2383,3433,0,1,0}-{2405,3447,0,1,0}-{2406,3440,0,1,0}-{2393,3451,1,1,0}-{2408,3437,1,1,0}-{2394,3500,1,1,0}-{2417,3483,1,1,0}-{2456,3425,0,1,0}-{2459,3421,0,1,0}-{2462,3431,0,1,0}-{2437,3478,0,1,0}-{2442,3465,0,1,0}-{2456,3467,0,1,0}-{2471,3496,0,1,0}-{2474,3508,0,1,0}-{2475,3471,0,1,0}-{2479,3468,0,1,0}-{2486,3470,0,1,0}-{2492,3474,0,1,0}-{2443,3464,1,1,0}-{2443,3502,1,1,0}-{2448,3496,1,1,0}-{2449,3506,1,1,0}-{2457,3498,1,1,0}-{2474,3498,1,1,0}-{2480,3502,1,1,0}-{2481,3482,1,1,0}-{2490,3503,1,1,0}-{2521,3169,0,1,0}-{2521,3171,0,1,0}-{2556,3226,0,1,0}-{2033,5530,1,1,0}-"
}, },
{ {
"npc_id": "68", "npc_id": "68",
"loc_data": "{2400,3514,1,1,0}-{2418,3485,1,1,0}-{2378,3423,0,1,0}-{2393,3435,0,1,0}-{2394,3425,0,1,0}-{2395,3449,0,1,0}-{2403,3433,0,1,0}-{2420,3429,0,1,0}-{2420,3438,0,1,0}-{2522,3172,0,1,0}-{2433,3474,0,1,0}-{2433,3492,0,1,0}-{2458,3497,0,1,0}-{2464,3505,0,1,0}-{2476,3458,0,1,0}-{2445,3502,1,1,0}-{2453,3488,1,1,0}-{2475,3502,1,1,0}-{2465,3490,2,1,0}-{2401,3357,0,1,0}-" "loc_data": "{2401,3357,0,1,0}-{2378,3423,0,1,0}-{2393,3435,0,1,0}-{2394,3425,0,1,0}-{2395,3449,0,1,0}-{2403,3433,0,1,0}-{2420,3429,0,1,0}-{2420,3438,0,1,0}-{2400,3514,1,1,0}-{2418,3485,1,1,0}-{2433,3474,0,1,0}-{2433,3492,0,1,0}-{2458,3497,0,1,0}-{2464,3505,0,1,0}-{2476,3458,0,1,0}-{2445,3502,1,1,0}-{2453,3488,1,1,0}-{2475,3502,1,1,0}-{2465,3490,2,1,0}-{2522,3172,0,1,0}-"
}, },
{ {
"npc_id": "73", "npc_id": "73",
@ -325,11 +325,11 @@
}, },
{ {
"npc_id": "100", "npc_id": "100",
"loc_data": "{2567,3440,0,1,3}-{2564,9653,0,1,0}-{2565,9655,0,1,0}-{2566,9626,0,1,0}-{2567,9653,0,1,0}-{2569,9633,0,1,0}-{2607,9621,0,1,0}-{2551,3408,0,1,6}-{2553,3405,0,1,3}-{2559,3452,0,1,3}-" "loc_data": "{2567,3440,0,1,3}-{2316,9392,0,1,0}-{2319,9367,0,1,0}-{2321,9402,0,1,0}-{2322,9387,0,1,0}-{2325,9360,0,1,0}-{2333,9346,0,1,0}-{2335,9382,0,1,0}-{2337,9387,0,1,0}-{2343,9360,0,1,0}-{2344,9369,0,1,0}-{2348,9390,0,1,0}-{2349,9380,0,1,0}-{2349,9402,0,1,0}-{2359,9345,0,1,0}-{2359,9359,0,1,0}-{2359,9374,0,1,0}-{2359,9382,0,1,0}-{2363,9403,0,1,0}-{2564,9653,0,1,0}-{2565,9655,0,1,0}-{2566,9626,0,1,0}-{2567,9653,0,1,0}-{2569,9633,0,1,0}-{2607,9621,0,1,0}-{2551,3408,0,1,6}-{2553,3405,0,1,3}-{2559,3452,0,1,3}-"
}, },
{ {
"npc_id": "101", "npc_id": "101",
"loc_data": "{2622,3389,0,1,4}-{2619,3390,0,1,4}-{2573,3437,0,1,3}-{2562,9659,0,1,0}-{2563,9661,0,1,0}-{2599,9626,0,1,0}-{2601,9627,0,1,0}-{2605,9621,0,1,0}-{2610,9620,0,1,0}-{2624,3391,0,1,1}-{2555,3407,0,1,3}-{2553,3457,0,1,3}-" "loc_data": "{2622,3389,0,1,4}-{2619,3390,0,1,4}-{2573,3437,0,1,3}-{2305,9385,0,1,0}-{2307,9359,0,1,0}-{2307,9403,0,1,0}-{2310,9396,0,1,0}-{2317,9371,0,1,0}-{2317,9383,0,1,0}-{2324,9404,0,1,0}-{2333,9366,0,1,0}-{2335,9393,0,1,0}-{2339,9403,0,1,0}-{2342,9347,0,1,0}-{2351,9359,0,1,0}-{2359,9392,0,1,0}-{2562,9659,0,1,0}-{2563,9661,0,1,0}-{2599,9626,0,1,0}-{2601,9627,0,1,0}-{2605,9621,0,1,0}-{2610,9620,0,1,0}-{2624,3391,0,1,1}-{2555,3407,0,1,3}-{2553,3457,0,1,3}-"
}, },
{ {
"npc_id": "102", "npc_id": "102",
@ -345,7 +345,7 @@
}, },
{ {
"npc_id": "105", "npc_id": "105",
"loc_data": "{3100,3594,0,1,6}-{3107,3608,0,1,2}-{3099,3602,0,1,0}-{2632,3280,0,1,1}-{2633,3274,0,1,3}-{2696,3329,0,1,4}-{2708,3336,0,1,4}-{3230,3500,0,1,5}-{2988,3671,0,1,0}-{3001,3674,0,1,0}-{2497,3164,0,1,2}-{2387,3376,0,1,0}-{2398,3366,0,1,0}-{2412,3378,0,1,0}-{2419,3372,0,1,0}-" "loc_data": "{3100,3594,0,1,6}-{3107,3608,0,1,2}-{3099,3602,0,1,0}-{2632,3280,0,1,1}-{2633,3274,0,1,3}-{2387,3376,0,1,0}-{2398,3366,0,1,0}-{2412,3378,0,1,0}-{2419,3372,0,1,0}-{2696,3329,0,1,4}-{2708,3336,0,1,4}-{3230,3500,0,1,5}-{2988,3671,0,1,0}-{3001,3674,0,1,0}-{2497,3164,0,1,2}-"
}, },
{ {
"npc_id": "106", "npc_id": "106",
@ -365,7 +365,7 @@
}, },
{ {
"npc_id": "110", "npc_id": "110",
"loc_data": "{2564,9887,0,1,4}-{2581,9897,0,1,1}-{2577,9888,0,1,1}-{3234,5497,0,1,1}-{3305,9400,0,1,1}-{3244,9356,0,1,1}-{3252,9370,0,1,1}-{3294,9375,0,1,3}-{3050,10337,0,1,4}-{3047,10340,0,1,4}-{3048,10346,0,1,4}-{3048,10346,0,1,3}-" "loc_data": "{2564,9887,0,1,4}-{2581,9897,0,1,1}-{2577,9888,0,1,1}-{3234,5497,0,1,1}-{3244,9356,0,1,1}-{3252,9370,0,1,1}-{3305,9400,0,1,1}-{3294,9375,0,1,3}-{3050,10337,0,1,4}-{3047,10340,0,1,4}-{3048,10346,0,1,4}-{3048,10346,0,1,3}-"
}, },
{ {
"npc_id": "111", "npc_id": "111",
@ -389,7 +389,7 @@
}, },
{ {
"npc_id": "117", "npc_id": "117",
"loc_data": "{2369,3404,0,1,0}-{3118,9845,0,1,4}-{3111,9844,0,1,6}-{3123,9845,0,1,3}-{3114,9833,0,1,3}-{3110,9841,0,1,1}-{3119,9839,0,1,1}-{3097,9832,0,1,0}-{3101,9832,0,1,1}-{3107,9829,0,1,3}-{3115,9831,0,1,4}-{3109,9835,0,1,4}-{2904,9734,0,1,0}-{2548,3146,0,1,1}-{2542,3145,0,1,4}-{2503,3150,0,1,4}-{3300,3649,0,1,0}-{3044,10321,0,1,2}-{3044,10316,0,1,4}-{3045,10308,0,1,4}-{3048,10317,0,1,2}-" "loc_data": "{3118,9845,0,1,4}-{3111,9844,0,1,6}-{3123,9845,0,1,3}-{3114,9833,0,1,3}-{3110,9841,0,1,1}-{3119,9839,0,1,1}-{3097,9832,0,1,0}-{3101,9832,0,1,1}-{3107,9829,0,1,3}-{3115,9831,0,1,4}-{3109,9835,0,1,4}-{2369,3404,0,1,0}-{2904,9734,0,1,0}-{2548,3146,0,1,1}-{2542,3145,0,1,4}-{2503,3150,0,1,4}-{3300,3649,0,1,0}-{3044,10321,0,1,2}-{3044,10316,0,1,4}-{3045,10308,0,1,4}-{3048,10317,0,1,2}-"
}, },
{ {
"npc_id": "118", "npc_id": "118",
@ -489,23 +489,23 @@
}, },
{ {
"npc_id": "153", "npc_id": "153",
"loc_data": "{2540,9820,0,1,0}-{2543,9813,0,1,0}-{2546,9817,0,1,0}-{2464,4421,0,1,0}-{2466,4423,0,1,0}-{2486,4464,0,1,0}-{2556,3444,0,1,0}-{3082,3077,0,1,0}-{3092,3082,0,1,0}-{3109,3090,0,1,0}-{3112,3093,0,1,0}-{2249,3260,0,1,0}-{2250,3257,0,1,0}-{2250,3259,0,1,0}-{2252,3261,0,1,0}-{2253,3256,0,1,0}-{2262,3226,0,1,0}-{2264,3225,0,1,0}-{2266,3229,0,1,0}-{2268,3227,0,1,0}-{2269,3222,0,1,0}-{2269,3224,0,1,0}-{2270,3223,0,1,0}-{2270,3226,0,1,0}-{3200,5954,0,1,0}-{3202,5958,0,1,0}-{3204,5955,0,1,0}-{3204,5959,0,1,0}-{3231,5967,0,1,0}-{3236,5974,0,1,0}-{3237,5968,0,1,0}-{3240,5992,0,1,0}-{3240,5994,0,1,0}-{3242,5990,0,1,0}-{3242,5993,0,1,0}-{3244,5994,0,1,0}-{3246,5989,0,1,0}-{2196,3191,0,1,0}-{2197,3189,0,1,0}-{2198,3181,0,1,0}-{2198,3187,0,1,0}-{2199,3183,0,1,0}-{2199,3187,0,1,0}-{2200,3185,0,1,0}-{2201,3185,0,1,0}-{2204,3180,0,1,0}-{2324,4599,0,1,0}-{2325,4597,0,1,0}-{2326,4589,0,1,0}-{2326,4595,0,1,0}-{2327,4591,0,1,0}-{2327,4595,0,1,0}-{2328,4593,0,1,0}-{2329,4593,0,1,0}-{2332,4588,0,1,0}-{2176,3202,0,1,0}-{2178,3206,0,1,0}-{2180,3203,0,1,0}-{2180,3207,0,1,0}-{2207,3215,0,1,0}-{2212,3222,0,1,0}-{2213,3216,0,1,0}-{2216,3240,0,1,0}-{2216,3242,0,1,0}-{2218,3238,0,1,0}-{2218,3241,0,1,0}-{2220,3242,0,1,0}-{2222,3237,0,1,0}-{2437,3425,0,1,0}-{2438,3422,0,1,0}-{2450,3419,0,1,0}-{2415,4466,0,1,0}-{2419,4427,0,1,0}-{2421,4468,0,1,0}-{2426,4431,0,1,0}-{3175,3226,0,1,0}-{3178,3228,0,1,0}-{3179,3226,0,1,0}-{3182,3251,0,1,0}-{3183,3254,0,1,0}-{2697,6204,0,1,0}-{2698,6201,0,1,0}-{2698,6203,0,1,0}-{2700,6205,0,1,0}-{2701,6200,0,1,0}-{2710,6170,0,1,0}-{2712,6169,0,1,0}-{2714,6173,0,1,0}-{2716,6171,0,1,0}-{2717,6166,0,1,0}-{2717,6168,0,1,0}-{2718,6167,0,1,0}-{2718,6170,0,1,0}-{2478,3373,0,1,0}-{2479,3381,0,1,0}-{2481,3384,0,1,0}-{2490,3371,0,1,0}-{2689,6083,0,1,0}-{2689,6088,0,1,0}-{2690,6094,0,1,0}-{2691,6084,0,1,0}-{2692,6081,0,1,0}-{2692,6090,0,1,0}-{2693,6083,0,1,0}-{2693,6085,0,1,0}-{2693,6090,0,1,0}-{2694,6083,0,1,0}-{2694,6086,0,1,0}-{2695,6082,0,1,0}-{2695,6085,0,1,0}-{2695,6088,0,1,0}-{2695,6091,0,1,0}-{2695,6121,0,1,0}-{2698,6121,0,1,0}-{2699,6082,0,1,0}-{2699,6085,0,1,0}-{2700,6084,0,1,0}-{2701,6085,0,1,0}-{2702,6081,0,1,0}-{2702,6083,0,1,0}-{2703,6119,0,1,0}-{2704,6117,0,1,0}-{2705,6121,0,1,0}-{2730,6091,0,1,0}-{2732,6095,0,1,0}-{2734,6090,0,1,0}-{2735,6094,0,1,0}-{2735,6097,0,1,0}-{2738,6098,0,1,0}-{2416,3404,0,1,0}-{2424,3410,0,1,0}-{2430,3405,0,1,0}-{1906,5223,0,1,0}-{1907,5221,0,1,0}-{3273,6012,0,1,0}-{3274,6009,0,1,0}-{3274,6011,0,1,0}-{3276,6013,0,1,0}-{3277,6008,0,1,0}-{3286,5978,0,1,0}-{3288,5977,0,1,0}-{3290,5981,0,1,0}-{3292,5979,0,1,0}-{3293,5974,0,1,0}-{3293,5976,0,1,0}-{3294,5975,0,1,0}-{3294,5978,0,1,0}-{2828,5091,0,1,0}-{2832,5108,0,1,0}-{2835,5079,0,1,0}-{2846,5067,0,1,0}-{2850,5111,0,1,0}-{2853,5086,0,1,0}-{2864,5071,0,1,0}-{2549,3139,0,1,0}-{2241,3139,0,1,0}-{2241,3144,0,1,0}-{2242,3150,0,1,0}-{2243,3140,0,1,0}-{2244,3137,0,1,0}-{2244,3146,0,1,0}-{2245,3139,0,1,0}-{2245,3141,0,1,0}-{2245,3146,0,1,0}-{2246,3139,0,1,0}-{2246,3142,0,1,0}-{2247,3138,0,1,0}-{2247,3141,0,1,0}-{2247,3144,0,1,0}-{2247,3147,0,1,0}-{2247,3177,0,1,0}-{2250,3177,0,1,0}-{2251,3138,0,1,0}-{2251,3141,0,1,0}-{2252,3140,0,1,0}-{2253,3141,0,1,0}-{2254,3137,0,1,0}-{2254,3139,0,1,0}-{2255,3175,0,1,0}-{2256,3173,0,1,0}-{2257,3177,0,1,0}-{2282,3147,0,1,0}-{2284,3151,0,1,0}-{2286,3146,0,1,0}-{2287,3150,0,1,0}-{2287,3153,0,1,0}-{2290,3154,0,1,0}-" "loc_data": "{3082,3077,0,1,0}-{3092,3082,0,1,0}-{3109,3090,0,1,0}-{3112,3093,0,1,0}-{2324,4599,0,1,0}-{2325,4597,0,1,0}-{2326,4589,0,1,0}-{2326,4595,0,1,0}-{2327,4591,0,1,0}-{2327,4595,0,1,0}-{2328,4593,0,1,0}-{2329,4593,0,1,0}-{2332,4588,0,1,0}-{2828,5091,0,1,0}-{2832,5108,0,1,0}-{2835,5079,0,1,0}-{2846,5067,0,1,0}-{2850,5111,0,1,0}-{2853,5086,0,1,0}-{2864,5071,0,1,0}-{3175,3226,0,1,0}-{3178,3228,0,1,0}-{3179,3226,0,1,0}-{3182,3251,0,1,0}-{3183,3254,0,1,0}-{2416,3404,0,1,0}-{2424,3410,0,1,0}-{2430,3405,0,1,0}-{2415,4466,0,1,0}-{2419,4427,0,1,0}-{2421,4468,0,1,0}-{2426,4431,0,1,0}-{1906,5223,0,1,0}-{1907,5221,0,1,0}-{2196,3191,0,1,0}-{2197,3189,0,1,0}-{2198,3181,0,1,0}-{2198,3187,0,1,0}-{2199,3183,0,1,0}-{2199,3187,0,1,0}-{2200,3185,0,1,0}-{2201,3185,0,1,0}-{2204,3180,0,1,0}-{2176,3202,0,1,0}-{2178,3206,0,1,0}-{2180,3203,0,1,0}-{2180,3207,0,1,0}-{2207,3215,0,1,0}-{2212,3222,0,1,0}-{2213,3216,0,1,0}-{2216,3240,0,1,0}-{2216,3242,0,1,0}-{2218,3238,0,1,0}-{2218,3241,0,1,0}-{2220,3242,0,1,0}-{2222,3237,0,1,0}-{2478,3373,0,1,0}-{2479,3381,0,1,0}-{2481,3384,0,1,0}-{2490,3371,0,1,0}-{2437,3425,0,1,0}-{2438,3422,0,1,0}-{2450,3419,0,1,0}-{2464,4421,0,1,0}-{2466,4423,0,1,0}-{2486,4464,0,1,0}-{3200,5954,0,1,0}-{3202,5958,0,1,0}-{3204,5955,0,1,0}-{3204,5959,0,1,0}-{3231,5967,0,1,0}-{3236,5974,0,1,0}-{3237,5968,0,1,0}-{3240,5992,0,1,0}-{3240,5994,0,1,0}-{3242,5990,0,1,0}-{3242,5993,0,1,0}-{3244,5994,0,1,0}-{3246,5989,0,1,0}-{2689,6083,0,1,0}-{2689,6088,0,1,0}-{2690,6094,0,1,0}-{2691,6084,0,1,0}-{2692,6081,0,1,0}-{2692,6090,0,1,0}-{2693,6083,0,1,0}-{2693,6085,0,1,0}-{2693,6090,0,1,0}-{2694,6083,0,1,0}-{2694,6086,0,1,0}-{2695,6082,0,1,0}-{2695,6085,0,1,0}-{2695,6088,0,1,0}-{2695,6091,0,1,0}-{2695,6121,0,1,0}-{2698,6121,0,1,0}-{2699,6082,0,1,0}-{2699,6085,0,1,0}-{2700,6084,0,1,0}-{2701,6085,0,1,0}-{2702,6081,0,1,0}-{2702,6083,0,1,0}-{2703,6119,0,1,0}-{2704,6117,0,1,0}-{2705,6121,0,1,0}-{2730,6091,0,1,0}-{2732,6095,0,1,0}-{2734,6090,0,1,0}-{2735,6094,0,1,0}-{2735,6097,0,1,0}-{2738,6098,0,1,0}-{2697,6204,0,1,0}-{2698,6201,0,1,0}-{2698,6203,0,1,0}-{2700,6205,0,1,0}-{2701,6200,0,1,0}-{2710,6170,0,1,0}-{2712,6169,0,1,0}-{2714,6173,0,1,0}-{2716,6171,0,1,0}-{2717,6166,0,1,0}-{2717,6168,0,1,0}-{2718,6167,0,1,0}-{2718,6170,0,1,0}-{2549,3139,0,1,0}-{2241,3139,0,1,0}-{2241,3144,0,1,0}-{2242,3150,0,1,0}-{2243,3140,0,1,0}-{2244,3137,0,1,0}-{2244,3146,0,1,0}-{2245,3139,0,1,0}-{2245,3141,0,1,0}-{2245,3146,0,1,0}-{2246,3139,0,1,0}-{2246,3142,0,1,0}-{2247,3138,0,1,0}-{2247,3141,0,1,0}-{2247,3144,0,1,0}-{2247,3147,0,1,0}-{2247,3177,0,1,0}-{2250,3177,0,1,0}-{2251,3138,0,1,0}-{2251,3141,0,1,0}-{2252,3140,0,1,0}-{2253,3141,0,1,0}-{2254,3137,0,1,0}-{2254,3139,0,1,0}-{2255,3175,0,1,0}-{2256,3173,0,1,0}-{2257,3177,0,1,0}-{2282,3147,0,1,0}-{2284,3151,0,1,0}-{2286,3146,0,1,0}-{2287,3150,0,1,0}-{2287,3153,0,1,0}-{2290,3154,0,1,0}-{2249,3260,0,1,0}-{2250,3257,0,1,0}-{2250,3259,0,1,0}-{2252,3261,0,1,0}-{2253,3256,0,1,0}-{2262,3226,0,1,0}-{2264,3225,0,1,0}-{2266,3229,0,1,0}-{2268,3227,0,1,0}-{2269,3222,0,1,0}-{2269,3224,0,1,0}-{2270,3223,0,1,0}-{2270,3226,0,1,0}-{2556,3444,0,1,0}-{3273,6012,0,1,0}-{3274,6009,0,1,0}-{3274,6011,0,1,0}-{3276,6013,0,1,0}-{3277,6008,0,1,0}-{3286,5978,0,1,0}-{3288,5977,0,1,0}-{3290,5981,0,1,0}-{3292,5979,0,1,0}-{3293,5974,0,1,0}-{3293,5976,0,1,0}-{3294,5975,0,1,0}-{3294,5978,0,1,0}-{2540,9820,0,1,0}-{2543,9813,0,1,0}-{2546,9817,0,1,0}-"
}, },
{ {
"npc_id": "154", "npc_id": "154",
"loc_data": "{2475,4459,0,1,0}-{2480,4458,0,1,0}-{2481,4454,0,1,0}-{2488,4465,0,1,0}-{3125,3084,0,1,0}-{2268,3228,0,1,0}-{3202,5955,0,1,0}-{3206,5953,0,1,0}-{3231,5976,0,1,0}-{3233,5974,0,1,0}-{3235,5970,0,1,0}-{3241,5996,0,1,0}-{3242,5988,0,1,0}-{3244,5992,0,1,0}-{2180,3173,0,1,0}-{2193,3187,0,1,0}-{2196,3184,0,1,0}-{2200,3181,0,1,0}-{2200,3188,0,1,0}-{2203,3189,0,1,0}-{2308,4581,0,1,0}-{2321,4595,0,1,0}-{2324,4592,0,1,0}-{2328,4589,0,1,0}-{2328,4596,0,1,0}-{2331,4597,0,1,0}-{2178,3203,0,1,0}-{2182,3201,0,1,0}-{2207,3224,0,1,0}-{2209,3222,0,1,0}-{2211,3218,0,1,0}-{2217,3244,0,1,0}-{2218,3236,0,1,0}-{2220,3240,0,1,0}-{2446,3396,0,1,0}-{2470,3397,0,1,0}-{2394,4453,0,1,0}-{2394,4457,0,1,0}-{2406,4447,0,1,0}-{2422,3489,0,1,0}-{2716,6172,0,1,0}-{2688,6092,0,1,0}-{2690,6087,0,1,0}-{2690,6095,0,1,0}-{2692,6082,0,1,0}-{2692,6088,0,1,0}-{2692,6123,0,1,0}-{2693,6081,0,1,0}-{2694,6125,0,1,0}-{2695,6087,0,1,0}-{2697,6086,0,1,0}-{2697,6087,0,1,0}-{2698,6081,0,1,0}-{2698,6124,0,1,0}-{2700,6083,0,1,0}-{2702,6084,0,1,0}-{2726,6085,0,1,0}-{2727,6081,0,1,0}-{2729,6083,0,1,0}-{2732,6084,0,1,0}-{2378,3418,0,1,0}-{2380,3422,0,1,0}-{2393,3442,0,1,0}-{2397,3440,0,1,0}-{2423,3399,0,1,0}-{2425,3406,0,1,0}-{2427,3402,0,1,0}-{1908,5222,0,1,0}-{3292,5980,0,1,0}-{2831,5070,0,1,0}-{2843,5106,0,1,0}-{2844,5080,0,1,0}-{2860,5079,0,1,0}-{2866,5104,0,1,0}-{2868,5096,0,1,0}-{2240,3148,0,1,0}-{2242,3143,0,1,0}-{2242,3151,0,1,0}-{2244,3138,0,1,0}-{2244,3144,0,1,0}-{2244,3179,0,1,0}-{2245,3137,0,1,0}-{2246,3181,0,1,0}-{2247,3143,0,1,0}-{2249,3142,0,1,0}-{2249,3143,0,1,0}-{2250,3137,0,1,0}-{2250,3180,0,1,0}-{2252,3139,0,1,0}-{2254,3140,0,1,0}-{2278,3141,0,1,0}-{2279,3137,0,1,0}-{2281,3139,0,1,0}-{2284,3140,0,1,0}-" "loc_data": "{3125,3084,0,1,0}-{2308,4581,0,1,0}-{2321,4595,0,1,0}-{2324,4592,0,1,0}-{2328,4589,0,1,0}-{2328,4596,0,1,0}-{2331,4597,0,1,0}-{2831,5070,0,1,0}-{2843,5106,0,1,0}-{2844,5080,0,1,0}-{2860,5079,0,1,0}-{2866,5104,0,1,0}-{2868,5096,0,1,0}-{2378,3418,0,1,0}-{2380,3422,0,1,0}-{2393,3442,0,1,0}-{2397,3440,0,1,0}-{2423,3399,0,1,0}-{2425,3406,0,1,0}-{2427,3402,0,1,0}-{2422,3489,0,1,0}-{2394,4453,0,1,0}-{2394,4457,0,1,0}-{2406,4447,0,1,0}-{1908,5222,0,1,0}-{2180,3173,0,1,0}-{2193,3187,0,1,0}-{2196,3184,0,1,0}-{2200,3181,0,1,0}-{2200,3188,0,1,0}-{2203,3189,0,1,0}-{2178,3203,0,1,0}-{2182,3201,0,1,0}-{2207,3224,0,1,0}-{2209,3222,0,1,0}-{2211,3218,0,1,0}-{2217,3244,0,1,0}-{2218,3236,0,1,0}-{2220,3240,0,1,0}-{2446,3396,0,1,0}-{2470,3397,0,1,0}-{2475,4459,0,1,0}-{2480,4458,0,1,0}-{2481,4454,0,1,0}-{2488,4465,0,1,0}-{3202,5955,0,1,0}-{3206,5953,0,1,0}-{3231,5976,0,1,0}-{3233,5974,0,1,0}-{3235,5970,0,1,0}-{3241,5996,0,1,0}-{3242,5988,0,1,0}-{3244,5992,0,1,0}-{2688,6092,0,1,0}-{2690,6087,0,1,0}-{2690,6095,0,1,0}-{2692,6082,0,1,0}-{2692,6088,0,1,0}-{2692,6123,0,1,0}-{2693,6081,0,1,0}-{2694,6125,0,1,0}-{2695,6087,0,1,0}-{2697,6086,0,1,0}-{2697,6087,0,1,0}-{2698,6081,0,1,0}-{2698,6124,0,1,0}-{2700,6083,0,1,0}-{2702,6084,0,1,0}-{2726,6085,0,1,0}-{2727,6081,0,1,0}-{2729,6083,0,1,0}-{2732,6084,0,1,0}-{2716,6172,0,1,0}-{2240,3148,0,1,0}-{2242,3143,0,1,0}-{2242,3151,0,1,0}-{2244,3138,0,1,0}-{2244,3144,0,1,0}-{2244,3179,0,1,0}-{2245,3137,0,1,0}-{2246,3181,0,1,0}-{2247,3143,0,1,0}-{2249,3142,0,1,0}-{2249,3143,0,1,0}-{2250,3137,0,1,0}-{2250,3180,0,1,0}-{2252,3139,0,1,0}-{2254,3140,0,1,0}-{2278,3141,0,1,0}-{2279,3137,0,1,0}-{2281,3139,0,1,0}-{2284,3140,0,1,0}-{2268,3228,0,1,0}-{3292,5980,0,1,0}-"
}, },
{ {
"npc_id": "155", "npc_id": "155",
"loc_data": "{3093,3086,0,1,0}-{3097,3085,0,1,0}-{3129,3091,0,1,0}-{3135,3084,0,1,0}-{2190,3180,0,1,0}-{2318,4588,0,1,0}-{2374,3469,0,1,0}-{2376,3466,0,1,0}-{3254,3230,0,1,0}-{1986,5564,0,1,0}-{2434,3516,0,1,0}-{2479,3501,0,1,0}-" "loc_data": "{3093,3086,0,1,0}-{3097,3085,0,1,0}-{3129,3091,0,1,0}-{3135,3084,0,1,0}-{2318,4588,0,1,0}-{2374,3469,0,1,0}-{2376,3466,0,1,0}-{2190,3180,0,1,0}-{3254,3230,0,1,0}-{2434,3516,0,1,0}-{2479,3501,0,1,0}-{1986,5564,0,1,0}-"
}, },
{ {
"npc_id": "156", "npc_id": "156",
"loc_data": "{2371,3460,0,1,0}-{2372,3456,0,1,0}-{2422,3467,0,1,0}-{2725,6127,0,1,0}-{2444,3491,0,1,0}-{2277,3183,0,1,0}-" "loc_data": "{2371,3460,0,1,0}-{2372,3456,0,1,0}-{2422,3467,0,1,0}-{2444,3491,0,1,0}-{2725,6127,0,1,0}-{2277,3183,0,1,0}-"
}, },
{ {
"npc_id": "157", "npc_id": "157",
"loc_data": "{2180,2798,0,1,0}-{2209,2812,0,1,0}-{2217,2780,0,1,0}-{1921,5931,0,1,0}-{1947,5908,0,1,0}-{1955,5949,0,1,0}-{1973,5888,0,1,0}-{1975,5918,0,1,0}-{1988,5870,0,1,0}-{2017,5884,0,1,0}-{2025,5852,0,1,0}-{2182,2979,0,1,0}-{2211,2963,0,1,0}-{1931,6038,0,1,0}-{1983,6037,0,1,0}-{2153,2810,0,1,0}-{2172,2806,0,1,0}-{1961,5882,0,1,0}-{1980,5878,0,1,0}-{2242,2788,0,1,0}-{2248,2858,0,1,0}-{2256,2828,0,1,0}-{2284,2874,0,1,0}-{2255,3216,0,1,0}-{2262,3210,0,1,0}-{2271,3204,0,1,0}-{2294,3219,0,1,0}-{2296,3206,0,1,0}-{2298,3201,0,1,0}-{2056,5930,0,1,0}-{2064,5900,0,1,0}-{2092,5946,0,1,0}-{2050,5860,0,1,0}-{3201,5986,0,1,0}-{3254,5972,0,1,0}-{3255,5986,0,1,0}-{3256,6008,0,1,0}-{3257,5973,0,1,0}-{3257,5975,0,1,0}-{3260,5974,0,1,0}-{3261,5977,0,1,0}-{3262,5957,0,1,0}-{2194,3158,0,1,0}-{2216,3188,0,1,0}-{2217,3190,0,1,0}-{2220,3158,0,1,0}-{2222,3141,0,1,0}-{2229,3138,0,1,0}-{2231,3181,0,1,0}-{2236,3155,0,1,0}-{2278,2956,0,1,0}-{2322,4566,0,1,0}-{2344,4596,0,1,0}-{2345,4598,0,1,0}-{2348,4566,0,1,0}-{2350,4549,0,1,0}-{2357,4546,0,1,0}-{2359,4589,0,1,0}-{2364,4563,0,1,0}-{2177,3234,0,1,0}-{2230,3220,0,1,0}-{2231,3234,0,1,0}-{2232,3256,0,1,0}-{2233,3221,0,1,0}-{2233,3223,0,1,0}-{2236,3222,0,1,0}-{2237,3225,0,1,0}-{2238,3205,0,1,0}-{2479,3396,0,1,0}-{2102,2942,0,1,0}-{2108,2919,0,1,0}-{2104,2873,0,1,0}-{2122,6032,0,1,0}-{1912,5945,0,1,0}-{3143,3210,0,1,0}-{3155,3253,0,1,0}-{3163,3261,0,1,0}-{3168,3258,0,1,0}-{2086,6028,0,1,0}-{2703,6160,0,1,0}-{2710,6154,0,1,0}-{2719,6148,0,1,0}-{2742,6163,0,1,0}-{2744,6150,0,1,0}-{2746,6145,0,1,0}-{2314,2960,0,1,0}-{2723,6105,0,1,0}-{2739,6123,0,1,0}-{2744,6108,0,1,0}-{2326,2894,0,1,0}-{2333,2920,0,1,0}-{3279,5968,0,1,0}-{3286,5962,0,1,0}-{3295,5956,0,1,0}-{3318,5971,0,1,0}-{3320,5958,0,1,0}-{3322,5953,0,1,0}-{2134,5966,0,1,0}-{2141,5992,0,1,0}-{2123,2966,0,1,0}-{2175,2965,0,1,0}-{1910,6014,0,1,0}-{1916,5991,0,1,0}-{2540,3167,0,1,0}-{2110,2955,0,1,0}-{1990,6051,0,1,0}-{2019,6035,0,1,0}-{2447,3467,0,1,0}-{2448,3469,0,1,0}-{1918,6027,0,1,0}-{2275,3161,0,1,0}-{2291,3179,0,1,0}-{2296,3164,0,1,0}-{2113,2859,0,1,0}-{2139,2836,0,1,0}-{2147,2877,0,1,0}-{2165,2816,0,1,0}-{2167,2846,0,1,0}-" "loc_data": "{2104,2873,0,1,0}-{2102,2942,0,1,0}-{2108,2919,0,1,0}-{2326,2894,0,1,0}-{2333,2920,0,1,0}-{2314,2960,0,1,0}-{2110,2955,0,1,0}-{2322,4566,0,1,0}-{2344,4596,0,1,0}-{2345,4598,0,1,0}-{2348,4566,0,1,0}-{2350,4549,0,1,0}-{2357,4546,0,1,0}-{2359,4589,0,1,0}-{2364,4563,0,1,0}-{2050,5860,0,1,0}-{2056,5930,0,1,0}-{2064,5900,0,1,0}-{2092,5946,0,1,0}-{2086,6028,0,1,0}-{2153,2810,0,1,0}-{2172,2806,0,1,0}-{2113,2859,0,1,0}-{2139,2836,0,1,0}-{2147,2877,0,1,0}-{2165,2816,0,1,0}-{2167,2846,0,1,0}-{2123,2966,0,1,0}-{2175,2965,0,1,0}-{3143,3210,0,1,0}-{3155,3253,0,1,0}-{3163,3261,0,1,0}-{3168,3258,0,1,0}-{1912,5945,0,1,0}-{2134,5966,0,1,0}-{2141,5992,0,1,0}-{1910,6014,0,1,0}-{1916,5991,0,1,0}-{2122,6032,0,1,0}-{1918,6027,0,1,0}-{2180,2798,0,1,0}-{2209,2812,0,1,0}-{2217,2780,0,1,0}-{2182,2979,0,1,0}-{2211,2963,0,1,0}-{2194,3158,0,1,0}-{2216,3188,0,1,0}-{2217,3190,0,1,0}-{2220,3158,0,1,0}-{2222,3141,0,1,0}-{2229,3138,0,1,0}-{2231,3181,0,1,0}-{2236,3155,0,1,0}-{2177,3234,0,1,0}-{2230,3220,0,1,0}-{2231,3234,0,1,0}-{2232,3256,0,1,0}-{2233,3221,0,1,0}-{2233,3223,0,1,0}-{2236,3222,0,1,0}-{2237,3225,0,1,0}-{2238,3205,0,1,0}-{2479,3396,0,1,0}-{2447,3467,0,1,0}-{2448,3469,0,1,0}-{1961,5882,0,1,0}-{1980,5878,0,1,0}-{1921,5931,0,1,0}-{1947,5908,0,1,0}-{1955,5949,0,1,0}-{1973,5888,0,1,0}-{1975,5918,0,1,0}-{3201,5986,0,1,0}-{3254,5972,0,1,0}-{3255,5986,0,1,0}-{3256,6008,0,1,0}-{3257,5973,0,1,0}-{3257,5975,0,1,0}-{3260,5974,0,1,0}-{3261,5977,0,1,0}-{3262,5957,0,1,0}-{1931,6038,0,1,0}-{1983,6037,0,1,0}-{2723,6105,0,1,0}-{2739,6123,0,1,0}-{2744,6108,0,1,0}-{2703,6160,0,1,0}-{2710,6154,0,1,0}-{2719,6148,0,1,0}-{2742,6163,0,1,0}-{2744,6150,0,1,0}-{2746,6145,0,1,0}-{2242,2788,0,1,0}-{2248,2858,0,1,0}-{2256,2828,0,1,0}-{2284,2874,0,1,0}-{2278,2956,0,1,0}-{2540,3167,0,1,0}-{2275,3161,0,1,0}-{2291,3179,0,1,0}-{2296,3164,0,1,0}-{2255,3216,0,1,0}-{2262,3210,0,1,0}-{2271,3204,0,1,0}-{2294,3219,0,1,0}-{2296,3206,0,1,0}-{2298,3201,0,1,0}-{1988,5870,0,1,0}-{2017,5884,0,1,0}-{2025,5852,0,1,0}-{3279,5968,0,1,0}-{3286,5962,0,1,0}-{3295,5956,0,1,0}-{3318,5971,0,1,0}-{3320,5958,0,1,0}-{3322,5953,0,1,0}-{1990,6051,0,1,0}-{2019,6035,0,1,0}-"
}, },
{ {
"npc_id": "158", "npc_id": "158",
@ -513,7 +513,7 @@
}, },
{ {
"npc_id": "159", "npc_id": "159",
"loc_data": "{2392,3475,0,1,0}-{2394,3506,0,1,0}-{2396,3471,0,1,0}-{2405,3499,0,1,0}-{2416,3487,0,1,0}-{2413,3445,1,1,0}-{2415,3435,1,1,0}-{2416,3416,1,1,0}-{2424,3434,1,1,0}-{2483,3500,1,1,0}-" "loc_data": "{2413,3445,1,1,0}-{2415,3435,1,1,0}-{2416,3416,1,1,0}-{2424,3434,1,1,0}-{2392,3475,0,1,0}-{2394,3506,0,1,0}-{2396,3471,0,1,0}-{2405,3499,0,1,0}-{2416,3487,0,1,0}-{2483,3500,1,1,0}-"
}, },
{ {
"npc_id": "160", "npc_id": "160",
@ -529,11 +529,11 @@
}, },
{ {
"npc_id": "163", "npc_id": "163",
"loc_data": "{2451,3414,0,1,0}-{2459,3395,0,1,0}-{2459,3438,0,1,0}-{2462,3395,0,1,0}-{2445,3429,1,1,0}-{2409,3470,1,1,0}-{2416,3466,1,1,0}-{2420,3466,1,1,0}-{2461,9895,0,1,0}-{2465,9894,0,1,0}-{2465,9899,0,1,0}-{2459,3385,0,1,0}-{2463,3385,0,1,0}-{2392,3454,0,1,0}-{2394,3436,0,1,0}-{2408,3452,0,1,0}-{2421,3413,0,1,0}-{2442,3489,0,1,0}-{2450,3485,0,1,0}-{2451,3507,0,1,0}-{2453,3490,0,1,0}-{2459,3503,0,1,0}-{2461,3509,0,1,0}-{2464,3465,0,1,0}-{2464,3468,0,1,0}-{2468,3465,0,1,0}-{2468,3468,0,1,0}-{2468,3506,0,1,0}-{2472,3484,0,1,0}-{2475,3490,0,1,0}-{2475,3502,0,1,0}-{2477,3512,0,1,0}-{2481,3485,0,1,0}-{2482,3501,0,1,0}-{2467,3495,1,1,0}-{2473,3495,1,1,0}-{2448,3497,2,1,0}-{2463,3480,2,1,0}-{2465,3497,2,1,0}-{2465,3510,2,1,0}-{2466,3480,2,1,0}-" "loc_data": "{2392,3454,0,1,0}-{2394,3436,0,1,0}-{2408,3452,0,1,0}-{2421,3413,0,1,0}-{2409,3470,1,1,0}-{2416,3466,1,1,0}-{2420,3466,1,1,0}-{2459,3385,0,1,0}-{2463,3385,0,1,0}-{2451,3414,0,1,0}-{2459,3395,0,1,0}-{2459,3438,0,1,0}-{2462,3395,0,1,0}-{2445,3429,1,1,0}-{2442,3489,0,1,0}-{2450,3485,0,1,0}-{2451,3507,0,1,0}-{2453,3490,0,1,0}-{2459,3503,0,1,0}-{2461,3509,0,1,0}-{2464,3465,0,1,0}-{2464,3468,0,1,0}-{2468,3465,0,1,0}-{2468,3468,0,1,0}-{2468,3506,0,1,0}-{2472,3484,0,1,0}-{2475,3490,0,1,0}-{2475,3502,0,1,0}-{2477,3512,0,1,0}-{2481,3485,0,1,0}-{2482,3501,0,1,0}-{2467,3495,1,1,0}-{2473,3495,1,1,0}-{2448,3497,2,1,0}-{2463,3480,2,1,0}-{2465,3497,2,1,0}-{2465,3510,2,1,0}-{2466,3480,2,1,0}-{2461,9895,0,1,0}-{2465,9894,0,1,0}-{2465,9899,0,1,0}-"
}, },
{ {
"npc_id": "164", "npc_id": "164",
"loc_data": "{2459,3392,0,1,0}-{2461,3422,0,1,0}-{2462,3392,0,1,0}-{2460,3382,0,1,0}-{2462,3382,0,1,0}-{2410,3416,0,1,0}-{2420,3435,0,1,0}-{2420,3447,0,1,0}-{2427,3410,0,1,0}-{2012,5535,2,1,0}-{2023,5544,2,1,0}-{2441,3497,0,1,0}-{2447,3510,0,1,0}-{2461,3487,0,1,0}-{2464,3472,0,1,0}-{2464,3489,0,1,0}-{2467,3489,0,1,0}-{2468,3472,0,1,0}-{2473,3503,0,1,0}-{2478,3497,0,1,0}-{2464,3496,1,1,0}-{2448,3495,2,1,0}-{2460,3487,2,1,0}-{2467,3494,2,1,0}-{2467,3510,2,1,0}-{2471,3496,2,1,0}-{2483,3495,2,1,0}-{2483,3497,2,1,0}-" "loc_data": "{2410,3416,0,1,0}-{2420,3435,0,1,0}-{2420,3447,0,1,0}-{2427,3410,0,1,0}-{2460,3382,0,1,0}-{2462,3382,0,1,0}-{2459,3392,0,1,0}-{2461,3422,0,1,0}-{2462,3392,0,1,0}-{2441,3497,0,1,0}-{2447,3510,0,1,0}-{2461,3487,0,1,0}-{2464,3472,0,1,0}-{2464,3489,0,1,0}-{2467,3489,0,1,0}-{2468,3472,0,1,0}-{2473,3503,0,1,0}-{2478,3497,0,1,0}-{2464,3496,1,1,0}-{2448,3495,2,1,0}-{2460,3487,2,1,0}-{2467,3494,2,1,0}-{2467,3510,2,1,0}-{2471,3496,2,1,0}-{2483,3495,2,1,0}-{2483,3497,2,1,0}-{2012,5535,2,1,0}-{2023,5544,2,1,0}-"
}, },
{ {
"npc_id": "166", "npc_id": "166",
@ -541,11 +541,11 @@
}, },
{ {
"npc_id": "168", "npc_id": "168",
"loc_data": "{2434,3436,0,1,0}-{2437,3451,0,1,0}-{2438,3427,0,1,0}-{2441,3411,0,1,0}-{2466,3449,0,1,0}-{2470,3399,0,1,0}-{2472,3400,0,1,0}-{2473,3412,0,1,0}-{2476,3454,0,1,0}-{2482,3397,0,1,0}-{2489,3401,0,1,0}-{2479,3407,1,1,0}-{2379,3482,0,1,0}-{2381,3496,0,1,0}-{2384,3497,0,1,0}-{2391,3476,0,1,0}-{2410,3496,0,1,0}-{2421,3481,0,1,0}-{2397,3514,1,1,0}-{2398,3451,1,1,0}-{2414,3447,1,1,0}-{2438,3465,0,1,0}-{2442,3505,0,1,0}-{2448,3486,0,1,0}-{2449,3487,0,1,0}-{2450,3489,0,1,0}-{2450,3505,0,1,0}-{2454,3465,0,1,0}-{2449,3486,1,1,0}-{2457,3488,1,1,0}-{2476,3488,1,1,0}-{2450,3496,2,1,0}-{2467,3488,2,1,0}-{2470,3503,2,1,0}-{2481,3498,2,1,0}-" "loc_data": "{2398,3451,1,1,0}-{2414,3447,1,1,0}-{2379,3482,0,1,0}-{2381,3496,0,1,0}-{2384,3497,0,1,0}-{2391,3476,0,1,0}-{2410,3496,0,1,0}-{2421,3481,0,1,0}-{2397,3514,1,1,0}-{2434,3436,0,1,0}-{2437,3451,0,1,0}-{2438,3427,0,1,0}-{2441,3411,0,1,0}-{2466,3449,0,1,0}-{2470,3399,0,1,0}-{2472,3400,0,1,0}-{2473,3412,0,1,0}-{2476,3454,0,1,0}-{2482,3397,0,1,0}-{2489,3401,0,1,0}-{2479,3407,1,1,0}-{2438,3465,0,1,0}-{2442,3505,0,1,0}-{2448,3486,0,1,0}-{2449,3487,0,1,0}-{2450,3489,0,1,0}-{2450,3505,0,1,0}-{2454,3465,0,1,0}-{2449,3486,1,1,0}-{2457,3488,1,1,0}-{2476,3488,1,1,0}-{2450,3496,2,1,0}-{2467,3488,2,1,0}-{2470,3503,2,1,0}-{2481,3498,2,1,0}-"
}, },
{ {
"npc_id": "169", "npc_id": "169",
"loc_data": "{2439,3433,0,1,0}-{2441,3449,0,1,0}-{2442,3428,0,1,0}-{2446,3403,0,1,0}-{2450,3416,0,1,0}-{2468,3441,0,1,0}-{2480,3408,0,1,0}-{2480,3406,1,1,0}-{2486,3400,1,1,0}-{2378,3482,0,1,0}-{2383,3496,0,1,0}-{2402,3507,0,1,0}-{2406,3476,0,1,0}-{2422,3485,0,1,0}-{2382,3506,1,1,0}-{2418,3472,1,1,0}-{2392,3450,1,1,0}-{2415,3415,1,1,0}-{2416,3434,1,1,0}-{2423,3425,1,1,0}-{2424,3442,1,1,0}-{2448,3489,0,1,0}-{2450,3488,0,1,0}-{2463,3508,0,1,0}-{2473,3489,0,1,0}-{2474,3457,0,1,0}-{2479,3503,0,1,0}-{2486,3467,0,1,0}-{2437,3463,1,1,0}-{2448,3489,1,1,0}-{2482,3508,1,1,0}-" "loc_data": "{2392,3450,1,1,0}-{2415,3415,1,1,0}-{2416,3434,1,1,0}-{2423,3425,1,1,0}-{2424,3442,1,1,0}-{2378,3482,0,1,0}-{2383,3496,0,1,0}-{2402,3507,0,1,0}-{2406,3476,0,1,0}-{2422,3485,0,1,0}-{2382,3506,1,1,0}-{2418,3472,1,1,0}-{2439,3433,0,1,0}-{2441,3449,0,1,0}-{2442,3428,0,1,0}-{2446,3403,0,1,0}-{2450,3416,0,1,0}-{2468,3441,0,1,0}-{2480,3408,0,1,0}-{2480,3406,1,1,0}-{2486,3400,1,1,0}-{2448,3489,0,1,0}-{2450,3488,0,1,0}-{2463,3508,0,1,0}-{2473,3489,0,1,0}-{2474,3457,0,1,0}-{2479,3503,0,1,0}-{2486,3467,0,1,0}-{2437,3463,1,1,0}-{2448,3489,1,1,0}-{2482,3508,1,1,0}-"
}, },
{ {
"npc_id": "170", "npc_id": "170",
@ -1357,11 +1357,11 @@
}, },
{ {
"npc_id": "479", "npc_id": "479",
"loc_data": "{1970,5522,3,1,0}-{2384,3481,0,1,0}-{2388,3473,0,1,0}-{2418,3474,3,1,0}-{2415,3433,3,1,0}-{2466,3500,2,1,0}-" "loc_data": "{2415,3433,3,1,0}-{2384,3481,0,1,0}-{2388,3473,0,1,0}-{2418,3474,3,1,0}-{2466,3500,2,1,0}-{1970,5522,3,1,0}-"
}, },
{ {
"npc_id": "480", "npc_id": "480",
"loc_data": "{1964,5522,3,1,0}-{2458,3417,1,1,0}-{2460,3417,1,1,0}-{2409,3507,0,1,0}-{2412,3474,3,1,0}-{2463,3504,2,1,0}-" "loc_data": "{2409,3507,0,1,0}-{2412,3474,3,1,0}-{2458,3417,1,1,0}-{2460,3417,1,1,0}-{2463,3504,2,1,0}-{1964,5522,3,1,0}-"
}, },
{ {
"npc_id": "481", "npc_id": "481",
@ -1387,6 +1387,10 @@
"npc_id": "490", "npc_id": "490",
"loc_data": "{1887,5026,0,0,6}-" "loc_data": "{1887,5026,0,0,6}-"
}, },
{
"npc_id": "492",
"loc_data": "{2464,3227,0,1,0}-"
},
{ {
"npc_id": "494", "npc_id": "494",
"loc_data": "{2615,3094,0,0,3}-{2615,3092,0,0,3}-{2615,3092,0,0,3}-{2615,3094,0,0,3}-{3122,3125,0,0,6}-{3120,3125,0,0,6}-{3090,3242,0,0,4}-{3090,3245,0,0,4}-{3090,3243,0,0,4}-{2618,3330,0,0,0}-{2619,3330,0,0,0}-{2584,3422,0,0,4}-{2584,3419,0,0,4}-{2584,3418,0,0,4}-{2657,3283,0,0,3}-{2657,3286,0,0,3}-{2807,3443,0,0,6}-{2810,3443,0,0,6}-" "loc_data": "{2615,3094,0,0,3}-{2615,3092,0,0,3}-{2615,3092,0,0,3}-{2615,3094,0,0,3}-{3122,3125,0,0,6}-{3120,3125,0,0,6}-{3090,3242,0,0,4}-{3090,3245,0,0,4}-{3090,3243,0,0,4}-{2618,3330,0,0,0}-{2619,3330,0,0,0}-{2584,3422,0,0,4}-{2584,3419,0,0,4}-{2584,3418,0,0,4}-{2657,3283,0,0,3}-{2657,3286,0,0,3}-{2807,3443,0,0,6}-{2810,3443,0,0,6}-"
@ -2433,11 +2437,11 @@
}, },
{ {
"npc_id": "839", "npc_id": "839",
"loc_data": "{3265,3066,0,1,0}-{3267,3066,0,1,0}-{3310,3068,0,1,0}-{3322,3011,0,1,0}-{3322,3052,0,1,0}-{3324,3030,0,1,0}-{3150,3044,0,1,0}-{3172,3009,0,1,0}-{3197,3012,0,1,0}-{3198,3040,0,1,0}-{3217,3092,0,1,0}-{3235,3074,0,1,0}-{3252,3125,0,1,0}-{3258,3078,0,1,0}-{3237,2968,0,1,0}-{3224,3013,0,1,0}-{3225,3034,0,1,0}-{3226,3060,0,1,0}-{3250,3057,0,1,0}-{3283,3108,0,1,0}-{3323,3094,0,1,0}-" "loc_data": "{3150,3044,0,1,0}-{3172,3009,0,1,0}-{3197,3012,0,1,0}-{3198,3040,0,1,0}-{3237,2968,0,1,0}-{3224,3013,0,1,0}-{3225,3034,0,1,0}-{3226,3060,0,1,0}-{3250,3057,0,1,0}-{3217,3092,0,1,0}-{3235,3074,0,1,0}-{3252,3125,0,1,0}-{3258,3078,0,1,0}-{3265,3066,0,1,0}-{3267,3066,0,1,0}-{3310,3068,0,1,0}-{3322,3011,0,1,0}-{3322,3052,0,1,0}-{3324,3030,0,1,0}-{3283,3108,0,1,0}-{3323,3094,0,1,0}-"
}, },
{ {
"npc_id": "840", "npc_id": "840",
"loc_data": "{3268,3052,0,1,0}-{3281,3056,0,1,0}-{3307,3055,0,1,0}-{3318,3020,0,1,0}-{3318,3040,0,1,0}-{3190,3054,0,1,0}-{3192,3016,0,1,0}-{3217,3111,0,1,0}-{3222,3086,0,1,0}-{3238,3101,0,1,0}-{3244,3080,0,1,0}-{3253,3116,0,1,0}-{3255,3095,0,1,0}-{3237,3000,0,1,0}-{3245,2960,0,1,0}-{3208,3032,0,1,0}-{3217,3064,0,1,0}-{3238,3015,0,1,0}-{3258,3063,0,1,0}-{3267,3077,0,1,0}-{3269,3110,0,1,0}-{3275,3094,0,1,0}-{3291,3078,0,1,0}-{3291,3100,0,1,0}-{3305,3089,0,1,0}-{3318,3078,0,1,0}-{3321,3104,0,1,0}-" "loc_data": "{3190,3054,0,1,0}-{3192,3016,0,1,0}-{3237,3000,0,1,0}-{3245,2960,0,1,0}-{3208,3032,0,1,0}-{3217,3064,0,1,0}-{3238,3015,0,1,0}-{3258,3063,0,1,0}-{3217,3111,0,1,0}-{3222,3086,0,1,0}-{3238,3101,0,1,0}-{3244,3080,0,1,0}-{3253,3116,0,1,0}-{3255,3095,0,1,0}-{3268,3052,0,1,0}-{3281,3056,0,1,0}-{3307,3055,0,1,0}-{3318,3020,0,1,0}-{3318,3040,0,1,0}-{3267,3077,0,1,0}-{3269,3110,0,1,0}-{3275,3094,0,1,0}-{3291,3078,0,1,0}-{3291,3100,0,1,0}-{3305,3089,0,1,0}-{3318,3078,0,1,0}-{3321,3104,0,1,0}-"
}, },
{ {
"npc_id": "841", "npc_id": "841",
@ -2781,7 +2785,7 @@
}, },
{ {
"npc_id": "1019", "npc_id": "1019",
"loc_data": "{3187,5555,0,1,4}-{3190,5563,0,1,1}-{3193,5555,0,1,3} -{3213,9377,0,1,3}-{3209,9397,0,1,4}-{3245,9401,0,1,6}- {3237,9402,0,1,2}-{3207,9349,0,1,3}-{3220,9347,0,1,6} -{3233,9359,0,1,4}-{3235,9354,0,0,6}-{3259,9370,0,1,1}- {3258,9387,0,1,6}-{2707,9880,0,1,0}-{2711,9876,0,1,0}- {2712,9871,0,1,0}-{2715,9874,0,1,0}-{2720,9871,0,1,0} -{2717,9880,0,1,0}-{2722,9879,0,1,0}-{2723,9875,0,1,0}- {3278,9368,0,1,5}-{3271,9359,0,1,3}-{3287,9359,0,1,5}- {3301,9394,0,1,5}-{3318,9352,0,1,5}-" "loc_data": "{3187,5555,0,1,4}-{3190,5563,0,1,1}-{3193,5555,0,1,3}-{3213,9377,0,1,3}-{3209,9397,0,1,4}-{3245,9401,0,1,6}-{3237,9402,0,1,2}-{3207,9349,0,1,3}-{3220,9347,0,1,6}-{3233,9359,0,1,4}-{3235,9354,0,0,6}-{3259,9370,0,1,1}-{3258,9387,0,1,6}-{2707,9880,0,1,0}-{2711,9876,0,1,0}-{2712,9871,0,1,0}-{2715,9874,0,1,0}-{2720,9871,0,1,0}-{2717,9880,0,1,0}-{2722,9879,0,1,0}-{2723,9875,0,1,0}-{3278,9368,0,1,5}-{3271,9359,0,1,3}-{3287,9359,0,1,5}-{3301,9394,0,1,5}-{3318,9352,0,1,5}-"
}, },
{ {
"npc_id": "1020", "npc_id": "1020",
@ -2829,7 +2833,7 @@
}, },
{ {
"npc_id": "1043", "npc_id": "1043",
"loc_data": "{3726,3381,0,0,0}-{3745,3359,0,0,0}-{3749,3365,0,0,0}-{3750,3354,0,0,0}-{3757,3363,0,0,0}-{3763,3337,0,0,0}-{3459,3457,0,0,0}-{3460,3462,0,0,0}-{3462,3459,0,0,0}-{3463,3490,0,0,0}-{3464,3511,0,0,0}-{3465,3466,0,0,0}-{3466,3495,0,0,0}-{3466,3497,0,0,0}-{3467,3485,0,0,0}-{3467,3497,0,0,0}-{3467,3509,0,0,0}-{3469,3470,0,0,0}-{3470,3469,0,0,0}-{3471,3477,0,0,0}-{3474,3505,0,0,0}-{3476,3507,0,0,0}-{3479,3511,0,0,0}-{3480,3468,0,0,0}-{3480,3470,0,0,0}-{3482,3469,0,0,0}-{3483,3511,0,0,0}-{3484,3464,0,0,0}-{3485,3509,0,0,0}-{3490,3461,0,0,0}-{3491,3460,0,0,0}-{3494,3461,0,0,0}-{3494,3512,0,0,0}-{3498,3461,0,0,0}-{3499,3513,0,0,0}-{3501,3513,0,0,0}-{3502,3464,0,0,0}-{3504,3463,0,0,0}-{3506,3512,0,0,0}-{3506,3515,0,0,0}-{3507,3515,0,0,0}-{3509,3512,0,0,0}-{3511,3488,0,0,0}-{3513,3467,0,0,0}-{3513,3487,0,0,0}-{3513,3489,0,0,0}-{3513,3503,0,0,0}-{3514,3490,0,0,0}-{3515,3495,0,0,0}-{3515,3499,0,0,0}-{3516,3469,0,0,0}-{3409,3369,0,0,0}-{3420,3349,0,0,0}-{3430,3340,0,0,0}-{3435,3330,0,0,0}-{3435,3359,0,0,0}-{3437,3374,0,0,0}-{3446,3385,0,0,0}-{3447,3388,0,0,0}-{3450,3370,0,0,0}-{3450,3387,0,0,0}-{3451,3336,0,0,0}-{3452,3355,0,0,0}-{3726,3289,0,0,0}-{3732,3291,0,0,0}-{3737,3280,0,0,0}-{3745,3285,0,0,0}-{3418,3420,0,0,0}-{3424,3452,0,0,0}-{3428,3409,0,0,0}-{3433,3416,0,0,0}-{3434,3394,0,0,0}-{3436,3407,0,0,0}-{3438,3414,0,0,0}-{3439,3449,0,0,0}-{3446,3439,0,0,0}-{3454,3410,0,0,0}-{3454,3423,0,0,0}-{2852,4561,0,0,0}-{2853,4566,0,0,0}-{2856,4568,0,0,0}-{2864,4561,0,0,0}-{2864,4563,0,0,0}-" "loc_data": "{2852,4561,0,0,0}-{2853,4566,0,0,0}-{2856,4568,0,0,0}-{2864,4561,0,0,0}-{2864,4563,0,0,0}-{3409,3369,0,0,0}-{3420,3349,0,0,0}-{3430,3340,0,0,0}-{3435,3330,0,0,0}-{3435,3359,0,0,0}-{3437,3374,0,0,0}-{3446,3385,0,0,0}-{3447,3388,0,0,0}-{3450,3370,0,0,0}-{3450,3387,0,0,0}-{3451,3336,0,0,0}-{3452,3355,0,0,0}-{3418,3420,0,0,0}-{3424,3452,0,0,0}-{3428,3409,0,0,0}-{3433,3416,0,0,0}-{3434,3394,0,0,0}-{3436,3407,0,0,0}-{3438,3414,0,0,0}-{3439,3449,0,0,0}-{3446,3439,0,0,0}-{3454,3410,0,0,0}-{3454,3423,0,0,0}-{3726,3289,0,0,0}-{3732,3291,0,0,0}-{3737,3280,0,0,0}-{3745,3285,0,0,0}-{3726,3381,0,0,0}-{3745,3359,0,0,0}-{3749,3365,0,0,0}-{3750,3354,0,0,0}-{3757,3363,0,0,0}-{3763,3337,0,0,0}-{3459,3457,0,0,0}-{3460,3462,0,0,0}-{3462,3459,0,0,0}-{3463,3490,0,0,0}-{3464,3511,0,0,0}-{3465,3466,0,0,0}-{3466,3495,0,0,0}-{3466,3497,0,0,0}-{3467,3485,0,0,0}-{3467,3497,0,0,0}-{3467,3509,0,0,0}-{3469,3470,0,0,0}-{3470,3469,0,0,0}-{3471,3477,0,0,0}-{3474,3505,0,0,0}-{3476,3507,0,0,0}-{3479,3511,0,0,0}-{3480,3468,0,0,0}-{3480,3470,0,0,0}-{3482,3469,0,0,0}-{3483,3511,0,0,0}-{3484,3464,0,0,0}-{3485,3509,0,0,0}-{3490,3461,0,0,0}-{3491,3460,0,0,0}-{3494,3461,0,0,0}-{3494,3512,0,0,0}-{3498,3461,0,0,0}-{3499,3513,0,0,0}-{3501,3513,0,0,0}-{3502,3464,0,0,0}-{3504,3463,0,0,0}-{3506,3512,0,0,0}-{3506,3515,0,0,0}-{3507,3515,0,0,0}-{3509,3512,0,0,0}-{3511,3488,0,0,0}-{3513,3467,0,0,0}-{3513,3487,0,0,0}-{3513,3489,0,0,0}-{3513,3503,0,0,0}-{3514,3490,0,0,0}-{3515,3495,0,0,0}-{3515,3499,0,0,0}-{3516,3469,0,0,0}-"
}, },
{ {
"npc_id": "1044", "npc_id": "1044",
@ -3345,11 +3349,11 @@
}, },
{ {
"npc_id": "1212", "npc_id": "1212",
"loc_data": "{2247,3226,0,0,0}-{2249,3227,0,0,0}-{2249,3258,0,0,0}-{2251,3260,0,0,0}-{2259,3211,0,0,0}-{2267,3223,0,0,0}-{2267,3225,0,0,0}-{2298,3262,0,0,0}-{3202,5990,0,0,0}-{3232,5977,0,0,0}-{3234,5969,0,0,0}-{3237,5995,0,0,0}-{3242,5997,0,0,0}-{3244,5995,0,0,0}-{3245,5995,0,0,0}-{2194,3189,0,0,0}-{2196,3187,0,0,0}-{2196,3189,0,0,0}-{2206,3164,0,0,0}-{2209,3164,0,0,0}-{2216,3142,0,0,0}-{2218,3139,0,0,0}-{2178,3238,0,0,0}-{2208,3225,0,0,0}-{2210,3217,0,0,0}-{2213,3243,0,0,0}-{2218,3245,0,0,0}-{2220,3243,0,0,0}-{2221,3243,0,0,0}-{2695,6170,0,0,0}-{2697,6171,0,0,0}-{2697,6202,0,0,0}-{2699,6204,0,0,0}-{2707,6155,0,0,0}-{2715,6167,0,0,0}-{2715,6169,0,0,0}-{2746,6206,0,0,0}-{2729,6086,0,0,0}-{2731,6080,0,0,0}-{2731,6089,0,0,0}-{2744,6139,0,0,0}-{3271,5978,0,0,0}-{3273,5979,0,0,0}-{3273,6010,0,0,0}-{3275,6012,0,0,0}-{3283,5963,0,0,0}-{3291,5975,0,0,0}-{3291,5977,0,0,0}-{3322,6014,0,0,0}-{2281,3142,0,0,0}-{2283,3136,0,0,0}-{2283,3145,0,0,0}-{2296,3195,0,0,0}-" "loc_data": "{2194,3189,0,0,0}-{2196,3187,0,0,0}-{2196,3189,0,0,0}-{2206,3164,0,0,0}-{2209,3164,0,0,0}-{2216,3142,0,0,0}-{2218,3139,0,0,0}-{2178,3238,0,0,0}-{2208,3225,0,0,0}-{2210,3217,0,0,0}-{2213,3243,0,0,0}-{2218,3245,0,0,0}-{2220,3243,0,0,0}-{2221,3243,0,0,0}-{3202,5990,0,0,0}-{3232,5977,0,0,0}-{3234,5969,0,0,0}-{3237,5995,0,0,0}-{3242,5997,0,0,0}-{3244,5995,0,0,0}-{3245,5995,0,0,0}-{2729,6086,0,0,0}-{2731,6080,0,0,0}-{2731,6089,0,0,0}-{2744,6139,0,0,0}-{2695,6170,0,0,0}-{2697,6171,0,0,0}-{2697,6202,0,0,0}-{2699,6204,0,0,0}-{2707,6155,0,0,0}-{2715,6167,0,0,0}-{2715,6169,0,0,0}-{2746,6206,0,0,0}-{2281,3142,0,0,0}-{2283,3136,0,0,0}-{2283,3145,0,0,0}-{2296,3195,0,0,0}-{2247,3226,0,0,0}-{2249,3227,0,0,0}-{2249,3258,0,0,0}-{2251,3260,0,0,0}-{2259,3211,0,0,0}-{2267,3223,0,0,0}-{2267,3225,0,0,0}-{2298,3262,0,0,0}-{3271,5978,0,0,0}-{3273,5979,0,0,0}-{3273,6010,0,0,0}-{3275,6012,0,0,0}-{3283,5963,0,0,0}-{3291,5975,0,0,0}-{3291,5977,0,0,0}-{3322,6014,0,0,0}-"
}, },
{ {
"npc_id": "1213", "npc_id": "1213",
"loc_data": "{2248,3225,0,0,0}-{2248,3227,0,0,0}-{2251,3257,0,0,0}-{2253,3259,0,0,0}-{2268,3224,0,0,0}-{2291,3262,0,0,0}-{3204,5988,0,0,0}-{3232,5979,0,0,0}-{3234,5967,0,0,0}-{3234,5976,0,0,0}-{3238,5996,0,0,0}-{3239,5997,0,0,0}-{3243,5996,0,0,0}-{2192,3188,0,0,0}-{2192,3191,0,0,0}-{2194,3190,0,0,0}-{2208,3163,0,0,0}-{2209,3163,0,0,0}-{2216,3136,0,0,0}-{2216,3140,0,0,0}-{2180,3236,0,0,0}-{2208,3227,0,0,0}-{2210,3215,0,0,0}-{2210,3224,0,0,0}-{2214,3244,0,0,0}-{2215,3245,0,0,0}-{2219,3244,0,0,0}-{2696,6169,0,0,0}-{2696,6171,0,0,0}-{2699,6201,0,0,0}-{2701,6203,0,0,0}-{2716,6168,0,0,0}-{2739,6206,0,0,0}-{2730,6084,0,0,0}-{3272,5977,0,0,0}-{3272,5979,0,0,0}-{3275,6009,0,0,0}-{3277,6011,0,0,0}-{3292,5976,0,0,0}-{3315,6014,0,0,0}-{2282,3140,0,0,0}-" "loc_data": "{2192,3188,0,0,0}-{2192,3191,0,0,0}-{2194,3190,0,0,0}-{2208,3163,0,0,0}-{2209,3163,0,0,0}-{2216,3136,0,0,0}-{2216,3140,0,0,0}-{2180,3236,0,0,0}-{2208,3227,0,0,0}-{2210,3215,0,0,0}-{2210,3224,0,0,0}-{2214,3244,0,0,0}-{2215,3245,0,0,0}-{2219,3244,0,0,0}-{3204,5988,0,0,0}-{3232,5979,0,0,0}-{3234,5967,0,0,0}-{3234,5976,0,0,0}-{3238,5996,0,0,0}-{3239,5997,0,0,0}-{3243,5996,0,0,0}-{2730,6084,0,0,0}-{2696,6169,0,0,0}-{2696,6171,0,0,0}-{2699,6201,0,0,0}-{2701,6203,0,0,0}-{2716,6168,0,0,0}-{2739,6206,0,0,0}-{2282,3140,0,0,0}-{2248,3225,0,0,0}-{2248,3227,0,0,0}-{2251,3257,0,0,0}-{2253,3259,0,0,0}-{2268,3224,0,0,0}-{2291,3262,0,0,0}-{3272,5977,0,0,0}-{3272,5979,0,0,0}-{3275,6009,0,0,0}-{3277,6011,0,0,0}-{3292,5976,0,0,0}-{3315,6014,0,0,0}-"
}, },
{ {
"npc_id": "1214", "npc_id": "1214",
@ -3771,6 +3775,10 @@
"npc_id": "1427", "npc_id": "1427",
"loc_data": "{2957,3025,0,0,0}-" "loc_data": "{2957,3025,0,0,0}-"
}, },
{
"npc_id": "1433",
"loc_data": "{2770,2789,0,1,6}-"
},
{ {
"npc_id": "1434", "npc_id": "1434",
"loc_data": "{2753,2770,0,1,6}-" "loc_data": "{2753,2770,0,1,6}-"
@ -4057,7 +4065,7 @@
}, },
{ {
"npc_id": "1633", "npc_id": "1633",
"loc_data": "{1837,3244,0,1,0}-{1836,3250,0,1,0}-{1845,3251,0,1,0}-{1845,3247,0,1,0}-{1849,3241,0,1,0}-{1853,3247,0,1,0}-{1848,3254,0,1,0}-{1858,3251,0,1,0}-{1935,3217,0,1,0}-{1933,3209,0,1,0}-{1930,3208,0,1,0}-{1929,3217,0,1,0}-{1927,3219,0,1,0}-{1924,3211,0,1,0}-{1920,3216,0,1,0}-{1925,3213,0,1,0}-{3263,9399,0,1,3}-{3274,9397,0,1,1}-{3275,9393,0,1,6}-{3271,9384,0,1,0}-{3270,9380,0,1,2}-{3283,9378,0,1,6}-{3285,9386,0,1,5}-{3284,9401,0,1,4}-{3278,9353,0,1,1}-{3299,9380,0,1,1}-{3319,9402,0,1,1}-{3305,9350,0,1,4}-{3248,9374,0,1,3}-{3253,9363,0,1,3}-{3249,9355,0,1,3}-{2761,10007,0,1,0}-{2757,10010,0,1,3}-{2763,10000,0,1,5}-{2760,10011,0,1,0}-{2761,9997,0,1,4}-" "loc_data": "{1837,3244,0,1,0}-{1836,3250,0,1,0}-{1845,3251,0,1,0}-{1845,3247,0,1,0}-{1849,3241,0,1,0}-{1853,3247,0,1,0}-{1848,3254,0,1,0}-{1858,3251,0,1,0}-{1935,3217,0,1,0}-{1933,3209,0,1,0}-{1930,3208,0,1,0}-{1929,3217,0,1,0}-{1927,3219,0,1,0}-{1924,3211,0,1,0}-{1920,3216,0,1,0}-{1925,3213,0,1,0}-{3263,9399,0,1,3}-{3248,9374,0,1,3}-{3253,9363,0,1,3}-{3249,9355,0,1,3}-{3274,9397,0,1,1}-{3275,9393,0,1,6}-{3271,9384,0,1,0}-{3270,9380,0,1,2}-{3283,9378,0,1,6}-{3285,9386,0,1,5}-{3284,9401,0,1,4}-{3278,9353,0,1,1}-{3299,9380,0,1,1}-{3319,9402,0,1,1}-{3305,9350,0,1,4}-{2761,10007,0,1,0}-{2757,10010,0,1,3}-{2763,10000,0,1,5}-{2760,10011,0,1,0}-{2761,9997,0,1,4}-"
}, },
{ {
"npc_id": "1634", "npc_id": "1634",
@ -4257,7 +4265,7 @@
}, },
{ {
"npc_id": "1752", "npc_id": "1752",
"loc_data": "{2437,3442,0,1,0}-{2449,3421,0,1,0}-{2457,3394,0,1,0}-{2464,3394,0,1,0}-{2480,3406,0,1,0}-{2432,3387,0,1,0}-{2442,3388,0,1,0}-{2453,3363,0,1,0}-{2463,3375,0,1,0}-{2381,3428,0,1,0}-{2392,3405,0,1,0}-{2413,3397,0,1,0}-{2415,3408,0,1,0}-{2463,3456,0,1,0}-{2468,3456,0,1,0}-" "loc_data": "{2381,3428,0,1,0}-{2392,3405,0,1,0}-{2413,3397,0,1,0}-{2415,3408,0,1,0}-{2432,3387,0,1,0}-{2442,3388,0,1,0}-{2453,3363,0,1,0}-{2463,3375,0,1,0}-{2437,3442,0,1,0}-{2449,3421,0,1,0}-{2457,3394,0,1,0}-{2464,3394,0,1,0}-{2480,3406,0,1,0}-{2463,3456,0,1,0}-{2468,3456,0,1,0}-"
}, },
{ {
"npc_id": "1754", "npc_id": "1754",
@ -4541,7 +4549,7 @@
}, },
{ {
"npc_id": "1874", "npc_id": "1874",
"loc_data": "{3371,9301,0,1,0}-{3371,9303,0,1,0}-{3371,9305,0,1,0}-{3371,9307,0,1,0}-{3371,9309,0,1,0}-{3373,9301,0,1,0}-{3373,9303,0,1,0}-{3373,9306,0,1,0}-{3373,9308,0,1,0}-{3375,9301,0,1,0}-{3375,9303,0,1,0}-{3375,9305,0,1,0}-{3375,9307,0,1,0}-{3375,9309,0,1,0}-{3403,2963,0,1,0}-{3429,2976,0,1,0}-{3328,2952,0,1,0}-{3328,2957,0,1,0}-{3331,2955,0,1,0}-{3331,2961,0,1,0}-{3266,2955,0,1,0}-{3271,2967,0,1,0}-{3279,2957,0,1,0}-{3280,2975,0,1,0}-{3284,2959,0,1,0}-{3294,2964,0,1,0}-{3295,2978,0,1,0}-{3305,2966,0,1,0}-{3307,2959,0,1,0}-{3309,2973,0,1,0}-{3396,3029,0,1,0}-{3397,3044,0,1,0}-{3398,3038,0,1,0}-" "loc_data": "{3328,2952,0,1,0}-{3328,2957,0,1,0}-{3331,2955,0,1,0}-{3331,2961,0,1,0}-{3371,9301,0,1,0}-{3371,9303,0,1,0}-{3371,9305,0,1,0}-{3371,9307,0,1,0}-{3371,9309,0,1,0}-{3373,9301,0,1,0}-{3373,9303,0,1,0}-{3373,9306,0,1,0}-{3373,9308,0,1,0}-{3375,9301,0,1,0}-{3375,9303,0,1,0}-{3375,9305,0,1,0}-{3375,9307,0,1,0}-{3375,9309,0,1,0}-{3403,2963,0,1,0}-{3429,2976,0,1,0}-{3396,3029,0,1,0}-{3397,3044,0,1,0}-{3398,3038,0,1,0}-{3266,2955,0,1,0}-{3271,2967,0,1,0}-{3279,2957,0,1,0}-{3280,2975,0,1,0}-{3284,2959,0,1,0}-{3294,2964,0,1,0}-{3295,2978,0,1,0}-{3305,2966,0,1,0}-{3307,2959,0,1,0}-{3309,2973,0,1,0}-"
}, },
{ {
"npc_id": "1875", "npc_id": "1875",
@ -4701,19 +4709,19 @@
}, },
{ {
"npc_id": "1961", "npc_id": "1961",
"loc_data": "{3156,5477,0,1,5}-{3162,5479,0,1,4}-{3159,5477,0,1,6}-{3150,5474,0,1,3}-{3147,5480,0,1,1}-{3159,5484,0,1,3}-{2764,4944,1,1,0}-{2796,4976,1,1,0}-{2798,4950,1,1,0}-{2806,4939,1,1,0}-{2832,4959,2,1,0}-{3201,9293,0,1,0}-{3205,9303,0,1,0}-{3206,9328,0,1,0}-{3207,9310,0,1,0}-{3211,9284,0,1,0}-{3220,9289,0,1,0}-{3224,9334,0,1,0}-{3249,9286,0,1,0}-{3251,9303,0,1,0}-{3252,9327,0,1,0}-{3252,9332,0,1,0}-{3255,9315,0,1,0}-{3261,9296,0,1,0}-{3261,9306,0,1,0}-" "loc_data": "{2832,4959,2,1,0}-{3156,5477,0,1,5}-{3162,5479,0,1,4}-{3159,5477,0,1,6}-{3150,5474,0,1,3}-{3147,5480,0,1,1}-{3159,5484,0,1,3}-{3201,9293,0,1,0}-{3205,9303,0,1,0}-{3206,9328,0,1,0}-{3207,9310,0,1,0}-{3211,9284,0,1,0}-{3220,9289,0,1,0}-{3224,9334,0,1,0}-{3249,9286,0,1,0}-{3251,9303,0,1,0}-{3252,9327,0,1,0}-{3252,9332,0,1,0}-{3255,9315,0,1,0}-{3261,9296,0,1,0}-{3261,9306,0,1,0}-{2764,4944,1,1,0}-{2796,4976,1,1,0}-{2798,4950,1,1,0}-{2806,4939,1,1,0}-"
}, },
{ {
"npc_id": "1962", "npc_id": "1962",
"loc_data": "{3168,5458,0,1,2}-{2900,4948,3,1,0}-{2762,4962,1,1,0}-{2763,4973,1,1,0}-{2780,4977,1,1,0}-{2787,4967,1,1,0}-{2796,4959,1,1,0}-{2838,4948,2,1,0}-{3205,9329,0,1,0}-{3210,9292,0,1,0}-{3221,9310,0,1,0}-{3225,9323,0,1,0}-{3243,9310,0,1,0}-{3246,9290,0,1,0}-{3255,9301,0,1,0}-{3255,9321,0,1,0}-{3261,9331,0,1,0}-" "loc_data": "{2838,4948,2,1,0}-{2900,4948,3,1,0}-{3168,5458,0,1,2}-{3205,9329,0,1,0}-{3210,9292,0,1,0}-{3221,9310,0,1,0}-{3225,9323,0,1,0}-{3243,9310,0,1,0}-{3246,9290,0,1,0}-{3255,9301,0,1,0}-{3255,9321,0,1,0}-{3261,9331,0,1,0}-{2762,4962,1,1,0}-{2763,4973,1,1,0}-{2780,4977,1,1,0}-{2787,4967,1,1,0}-{2796,4959,1,1,0}-"
}, },
{ {
"npc_id": "1963", "npc_id": "1963",
"loc_data": "{3166,5465,0,1,7}-{3168,5462,0,1,0}-{3167,5467,0,1,1}-{3167,5467,0,1,7}-{2926,4965,3,1,0}-{2771,4947,1,1,0}-{2775,4963,1,1,0}-{2781,4948,1,1,0}-{2792,4942,1,1,0}-{2798,4954,1,1,0}-{2858,4964,2,1,0}-{3204,9309,0,1,0}-{3214,9333,0,1,0}-{3219,9297,0,1,0}-{3239,9300,0,1,0}-{3240,9286,0,1,0}-{3240,9330,0,1,0}-{3245,9306,0,1,0}-{3250,9316,0,1,0}-{3250,9317,0,1,0}-{3257,9290,0,1,0}-" "loc_data": "{2858,4964,2,1,0}-{2926,4965,3,1,0}-{3166,5465,0,1,7}-{3168,5462,0,1,0}-{3167,5467,0,1,1}-{3167,5467,0,1,7}-{3204,9309,0,1,0}-{3214,9333,0,1,0}-{3219,9297,0,1,0}-{3239,9300,0,1,0}-{3240,9286,0,1,0}-{3240,9330,0,1,0}-{3245,9306,0,1,0}-{3250,9316,0,1,0}-{3250,9317,0,1,0}-{3257,9290,0,1,0}-{2771,4947,1,1,0}-{2775,4963,1,1,0}-{2781,4948,1,1,0}-{2792,4942,1,1,0}-{2798,4954,1,1,0}-"
}, },
{ {
"npc_id": "1964", "npc_id": "1964",
"loc_data": "{2761,4950,1,1,0}-{2765,4938,1,1,0}-{2770,4955,1,1,0}-{2772,4940,1,1,0}-{2777,4943,1,1,0}-{2782,4967,1,1,0}-{2797,4965,1,1,0}-{2799,4937,1,1,0}-{2799,4941,1,1,0}-{2807,4968,1,1,0}-{2808,4975,1,1,0}-{2809,4953,1,1,0}-{2864,4946,2,1,0}-{3202,9283,0,1,0}-{3206,9333,0,1,0}-{3209,9299,0,1,0}-{3219,9301,0,1,0}-{3226,9285,0,1,0}-{3227,9303,0,1,0}-{3228,9293,0,1,0}-{3229,9333,0,1,0}-{3237,9293,0,1,0}-{3246,9321,0,1,0}-{3256,9296,0,1,0}-{3260,9285,0,1,0}-{3262,9317,0,1,0}-" "loc_data": "{2864,4946,2,1,0}-{3202,9283,0,1,0}-{3206,9333,0,1,0}-{3209,9299,0,1,0}-{3219,9301,0,1,0}-{3226,9285,0,1,0}-{3227,9303,0,1,0}-{3228,9293,0,1,0}-{3229,9333,0,1,0}-{3237,9293,0,1,0}-{3246,9321,0,1,0}-{3256,9296,0,1,0}-{3260,9285,0,1,0}-{3262,9317,0,1,0}-{2761,4950,1,1,0}-{2765,4938,1,1,0}-{2770,4955,1,1,0}-{2772,4940,1,1,0}-{2777,4943,1,1,0}-{2782,4967,1,1,0}-{2797,4965,1,1,0}-{2799,4937,1,1,0}-{2799,4941,1,1,0}-{2807,4968,1,1,0}-{2808,4975,1,1,0}-{2809,4953,1,1,0}-"
}, },
{ {
"npc_id": "1970", "npc_id": "1970",
@ -4725,7 +4733,7 @@
}, },
{ {
"npc_id": "1973", "npc_id": "1973",
"loc_data": "{2693,5075,0,1,0}-{2695,5089,0,1,0}-{2697,5096,0,1,0}-{2703,5064,0,1,0}-{2710,5105,0,1,0}-{2719,5078,0,1,0}-{2719,5112,0,1,0}-{2720,5096,0,1,0}-{2726,5086,0,1,0}-{2729,5096,0,1,0}-{2735,5061,0,1,0}-{2740,5069,0,1,0}-{2740,5085,0,1,0}-{2746,5092,0,1,0}-{2746,5114,0,1,0}-{2626,5065,0,1,0}-{2637,5099,0,1,0}-{2638,5058,0,1,0}-{2644,5090,0,1,0}-{2658,5082,0,1,0}-{2666,5097,0,1,0}-{2680,5074,0,1,0}-" "loc_data": "{2626,5065,0,1,0}-{2637,5099,0,1,0}-{2638,5058,0,1,0}-{2644,5090,0,1,0}-{2658,5082,0,1,0}-{2666,5097,0,1,0}-{2680,5074,0,1,0}-{2693,5075,0,1,0}-{2695,5089,0,1,0}-{2697,5096,0,1,0}-{2703,5064,0,1,0}-{2710,5105,0,1,0}-{2719,5078,0,1,0}-{2719,5112,0,1,0}-{2720,5096,0,1,0}-{2726,5086,0,1,0}-{2729,5096,0,1,0}-{2735,5061,0,1,0}-{2740,5069,0,1,0}-{2740,5085,0,1,0}-{2746,5092,0,1,0}-{2746,5114,0,1,0}-"
}, },
{ {
"npc_id": "1976", "npc_id": "1976",
@ -4737,11 +4745,11 @@
}, },
{ {
"npc_id": "1993", "npc_id": "1993",
"loc_data": "{3264,2886,0,1,0}-{3267,2891,0,1,0}-{3268,2881,0,1,0}-{3269,2885,0,1,0}-{3296,2912,0,1,0}-{3297,2919,0,1,0}-{3301,2920,0,1,0}-{3308,2916,0,1,0}-{3258,2868,0,1,0}-{3259,2823,0,1,0}-{3259,2845,0,1,0}-{3260,2830,0,1,0}-{3260,2859,0,1,0}-{3261,2819,0,1,0}-{3261,2851,0,1,0}-{3337,2922,0,1,0}-{3343,2931,0,1,0}-{3349,2922,0,1,0}-{3351,2927,0,1,0}-{3357,2922,0,1,0}-{3359,2927,0,1,0}-{3364,2937,0,1,0}-{3268,2854,0,1,0}-{3269,2827,0,1,0}-{3269,2866,0,1,0}-{3269,2875,0,1,0}-{3270,2818,0,1,0}-{3272,2841,0,1,0}-{3276,2842,0,1,0}-{3282,2839,0,1,0}-{3290,2847,0,1,0}-" "loc_data": "{3337,2922,0,1,0}-{3343,2931,0,1,0}-{3349,2922,0,1,0}-{3351,2927,0,1,0}-{3357,2922,0,1,0}-{3359,2927,0,1,0}-{3364,2937,0,1,0}-{3258,2868,0,1,0}-{3259,2823,0,1,0}-{3259,2845,0,1,0}-{3260,2830,0,1,0}-{3260,2859,0,1,0}-{3261,2819,0,1,0}-{3261,2851,0,1,0}-{3268,2854,0,1,0}-{3269,2827,0,1,0}-{3269,2866,0,1,0}-{3269,2875,0,1,0}-{3270,2818,0,1,0}-{3272,2841,0,1,0}-{3276,2842,0,1,0}-{3282,2839,0,1,0}-{3290,2847,0,1,0}-{3264,2886,0,1,0}-{3267,2891,0,1,0}-{3268,2881,0,1,0}-{3269,2885,0,1,0}-{3296,2912,0,1,0}-{3297,2919,0,1,0}-{3301,2920,0,1,0}-{3308,2916,0,1,0}-"
}, },
{ {
"npc_id": "1994", "npc_id": "1994",
"loc_data": "{3265,2931,0,1,0}-{3265,2935,0,1,0}-{3268,2932,0,1,0}-{3268,2935,0,1,0}-{3316,2900,0,1,0}-{3319,2897,0,1,0}-{3320,2901,0,1,0}-{3321,2899,0,1,0}-{3212,2863,0,1,0}-{3213,2866,0,1,0}-{3215,2863,0,1,0}-{3216,2865,0,1,0}-{3216,2868,0,1,0}-{3218,2831,0,1,0}-{3220,2830,0,1,0}-{3220,2833,0,1,0}-{3221,2831,0,1,0}-{3238,2845,0,1,0}-{3240,2845,0,1,0}-{3241,2843,0,1,0}-{3241,2847,0,1,0}-{3400,2997,0,1,0}-{3402,2999,0,1,0}-{3403,2997,0,1,0}-{3445,2993,0,1,0}-{3446,2992,0,1,0}-{3448,2991,0,1,0}-{3448,2994,0,1,0}-{3329,2933,0,1,0}-{3330,2931,0,1,0}-{3331,2933,0,1,0}-{3343,2895,0,1,0}-{3345,2896,0,1,0}-{3346,2894,0,1,0}-{3348,2894,0,1,0}-{3376,2934,0,1,0}-{3378,2933,0,1,0}-{3378,2935,0,1,0}-{3381,2907,0,1,0}-{3383,2905,0,1,0}-{3383,2908,0,1,0}-{3384,2907,0,1,0}-{3306,2817,0,1,0}-{3308,2818,0,1,0}-{3309,2816,0,1,0}-{3312,2817,0,1,0}-{3314,2861,0,1,0}-{3319,2873,0,1,0}-{3321,2871,0,1,0}-{3324,2870,0,1,0}-{3327,2858,0,1,0}-{3406,3014,0,1,0}-{3408,3013,0,1,0}-{3408,3016,0,1,0}-{3410,3015,0,1,0}-" "loc_data": "{3329,2933,0,1,0}-{3330,2931,0,1,0}-{3331,2933,0,1,0}-{3343,2895,0,1,0}-{3345,2896,0,1,0}-{3346,2894,0,1,0}-{3348,2894,0,1,0}-{3376,2934,0,1,0}-{3378,2933,0,1,0}-{3378,2935,0,1,0}-{3381,2907,0,1,0}-{3383,2905,0,1,0}-{3383,2908,0,1,0}-{3384,2907,0,1,0}-{3400,2997,0,1,0}-{3402,2999,0,1,0}-{3403,2997,0,1,0}-{3445,2993,0,1,0}-{3446,2992,0,1,0}-{3448,2991,0,1,0}-{3448,2994,0,1,0}-{3406,3014,0,1,0}-{3408,3013,0,1,0}-{3408,3016,0,1,0}-{3410,3015,0,1,0}-{3212,2863,0,1,0}-{3213,2866,0,1,0}-{3215,2863,0,1,0}-{3216,2865,0,1,0}-{3216,2868,0,1,0}-{3218,2831,0,1,0}-{3220,2830,0,1,0}-{3220,2833,0,1,0}-{3221,2831,0,1,0}-{3238,2845,0,1,0}-{3240,2845,0,1,0}-{3241,2843,0,1,0}-{3241,2847,0,1,0}-{3306,2817,0,1,0}-{3308,2818,0,1,0}-{3309,2816,0,1,0}-{3312,2817,0,1,0}-{3314,2861,0,1,0}-{3319,2873,0,1,0}-{3321,2871,0,1,0}-{3324,2870,0,1,0}-{3327,2858,0,1,0}-{3265,2931,0,1,0}-{3265,2935,0,1,0}-{3268,2932,0,1,0}-{3268,2935,0,1,0}-{3316,2900,0,1,0}-{3319,2897,0,1,0}-{3320,2901,0,1,0}-{3321,2899,0,1,0}-"
}, },
{ {
"npc_id": "1995", "npc_id": "1995",
@ -4893,7 +4901,7 @@
}, },
{ {
"npc_id": "2057", "npc_id": "2057",
"loc_data": "{2442,9436,2,1,0}-{2458,9413,2,1,0}-{2470,9435,2,1,0}-{2472,9460,2,1,0}-{2483,9413,2,1,0}-{2485,9447,2,1,0}-{2480,3046,0,1,0}-{2453,9393,2,1,0}-{2461,9403,2,1,0}-{2464,9380,2,1,0}-" "loc_data": "{2480,3046,0,1,0}-{2453,9393,2,1,0}-{2461,9403,2,1,0}-{2464,9380,2,1,0}-{2442,9436,2,1,0}-{2458,9413,2,1,0}-{2470,9435,2,1,0}-{2472,9460,2,1,0}-{2483,9413,2,1,0}-{2485,9447,2,1,0}-"
}, },
{ {
"npc_id": "2058", "npc_id": "2058",
@ -6313,7 +6321,7 @@
}, },
{ {
"npc_id": "2803", "npc_id": "2803",
"loc_data": "{3387,3068,0,1,0}-{3387,3017,0,1,0}-{3404,3062,0,1,0}-{3441,3061,0,1,0}-{3445,3034,0,1,0}-{3338,2804,0,1,0}-{3339,2815,0,1,0}-{3342,2809,0,1,0}-{3344,2800,0,1,0}-{3348,2809,0,1,0}-{3349,2814,0,1,0}-{3357,2813,0,1,0}-{3357,2806,0,1,0}-{3365,2815,0,1,0}-" "loc_data": "{3338,2804,0,1,0}-{3339,2815,0,1,0}-{3342,2809,0,1,0}-{3344,2800,0,1,0}-{3348,2809,0,1,0}-{3349,2814,0,1,0}-{3357,2813,0,1,0}-{3357,2806,0,1,0}-{3365,2815,0,1,0}-{3387,3068,0,1,0}-{3387,3017,0,1,0}-{3404,3062,0,1,0}-{3441,3061,0,1,0}-{3445,3034,0,1,0}-"
}, },
{ {
"npc_id": "2804", "npc_id": "2804",
@ -7233,7 +7241,7 @@
}, },
{ {
"npc_id": "3675", "npc_id": "3675",
"loc_data": "{3333,2864,0,1,1}-{3335,2860,0,1,2}-{3338,2864,0,1,4}-{3215,2841,0,1,0}-{3217,2845,0,1,0}-{3220,2841,0,1,0}-{3295,2866,0,1,0}-{3297,2863,0,1,0}-{3301,2864,0,1,0}-{3336,2778,0,1,0}-{3343,2793,0,1,0}-{3367,2791,0,1,0}-" "loc_data": "{3336,2778,0,1,0}-{3343,2793,0,1,0}-{3367,2791,0,1,0}-{3333,2864,0,1,1}-{3335,2860,0,1,2}-{3338,2864,0,1,4}-{3215,2841,0,1,0}-{3217,2845,0,1,0}-{3220,2841,0,1,0}-{3295,2866,0,1,0}-{3297,2863,0,1,0}-{3301,2864,0,1,0}-"
}, },
{ {
"npc_id": "3677", "npc_id": "3677",
@ -8241,19 +8249,19 @@
}, },
{ {
"npc_id": "4690", "npc_id": "4690",
"loc_data": "{2372,3401,0,1,0}-{3104,3875,0,1,2}-{2912,9731,0,1,0}-{3256,3624,0,1,5}-{3308,3661,0,1,2}-" "loc_data": "{3104,3875,0,1,2}-{2372,3401,0,1,0}-{2912,9731,0,1,0}-{3256,3624,0,1,5}-{3308,3661,0,1,2}-"
}, },
{ {
"npc_id": "4691", "npc_id": "4691",
"loc_data": "{2371,3398,0,1,0}-{3110,3854,0,1,0}-" "loc_data": "{3110,3854,0,1,0}-{2371,3398,0,1,0}-"
}, },
{ {
"npc_id": "4692", "npc_id": "4692",
"loc_data": "{2372,3395,0,1,0}-{3116,3858,0,1,0}-" "loc_data": "{3116,3858,0,1,0}-{2372,3395,0,1,0}-"
}, },
{ {
"npc_id": "4693", "npc_id": "4693",
"loc_data": "{2369,3394,0,1,0}-{3094,3849,0,1,0}-{2912,9741,0,1,0}-{2906,9736,0,1,0}-" "loc_data": "{3094,3849,0,1,0}-{2369,3394,0,1,0}-{2912,9741,0,1,0}-{2906,9736,0,1,0}-"
}, },
{ {
"npc_id": "4694", "npc_id": "4694",
@ -9005,7 +9013,7 @@
}, },
{ {
"npc_id": "5359", "npc_id": "5359",
"loc_data": "{2693,5065,0,1,0}-{2693,5089,0,1,0}-{2704,5091,0,1,0}-{2710,5095,0,1,0}-{2712,5076,0,1,0}-{2714,5108,0,1,0}-{2716,5092,0,1,0}-{2719,5071,0,1,0}-{2722,5061,0,1,0}-{2732,5087,0,1,0}-{2739,5078,0,1,0}-{2740,5104,0,1,0}-{2747,5083,0,1,0}-{2627,5093,0,1,0}-{2629,5061,0,1,0}-{2638,5090,0,1,0}-{2651,5105,0,1,0}-{2654,5079,0,1,0}-{2663,5116,0,1,0}-{2683,5059,0,1,0}-{2685,5111,0,1,0}-" "loc_data": "{2627,5093,0,1,0}-{2629,5061,0,1,0}-{2638,5090,0,1,0}-{2651,5105,0,1,0}-{2654,5079,0,1,0}-{2663,5116,0,1,0}-{2683,5059,0,1,0}-{2685,5111,0,1,0}-{2693,5065,0,1,0}-{2693,5089,0,1,0}-{2704,5091,0,1,0}-{2710,5095,0,1,0}-{2712,5076,0,1,0}-{2714,5108,0,1,0}-{2716,5092,0,1,0}-{2719,5071,0,1,0}-{2722,5061,0,1,0}-{2732,5087,0,1,0}-{2739,5078,0,1,0}-{2740,5104,0,1,0}-{2747,5083,0,1,0}-"
}, },
{ {
"npc_id": "5361", "npc_id": "5361",
@ -10289,11 +10297,11 @@
}, },
{ {
"npc_id": "6050", "npc_id": "6050",
"loc_data": "{3264,3008,0,1,0}-{3266,3008,0,1,0}-{3285,3066,0,1,0}-{3322,3032,0,1,0}-{3323,3055,0,1,0}-{3325,3010,0,1,0}-{3174,3010,0,1,0}-{3198,3061,0,1,0}-{3199,3016,0,1,0}-{3219,3125,0,1,0}-{3233,3075,0,1,0}-{3250,3126,0,1,0}-{3223,3011,0,1,0}-{3228,3060,0,1,0}-{3251,3059,0,1,0}-{3310,3076,0,1,0}-" "loc_data": "{3174,3010,0,1,0}-{3198,3061,0,1,0}-{3199,3016,0,1,0}-{3223,3011,0,1,0}-{3228,3060,0,1,0}-{3251,3059,0,1,0}-{3219,3125,0,1,0}-{3233,3075,0,1,0}-{3250,3126,0,1,0}-{3264,3008,0,1,0}-{3266,3008,0,1,0}-{3285,3066,0,1,0}-{3322,3032,0,1,0}-{3323,3055,0,1,0}-{3325,3010,0,1,0}-{3310,3076,0,1,0}-"
}, },
{ {
"npc_id": "6051", "npc_id": "6051",
"loc_data": "{3266,3010,0,1,0}-{3267,3068,0,1,0}-{3287,3065,0,1,0}-{3287,3067,0,1,0}-{3310,3070,0,1,0}-{3312,3068,0,1,0}-{3320,3054,0,1,0}-{3324,3013,0,1,0}-{3325,3033,0,1,0}-{3153,3047,0,1,0}-{3196,3063,0,1,0}-{3199,3036,0,1,0}-{3215,3092,0,1,0}-{3217,3124,0,1,0}-{3260,3077,0,1,0}-{3223,3035,0,1,0}-{3225,3011,0,1,0}-{3283,3084,0,1,0}-" "loc_data": "{3153,3047,0,1,0}-{3196,3063,0,1,0}-{3199,3036,0,1,0}-{3223,3035,0,1,0}-{3225,3011,0,1,0}-{3215,3092,0,1,0}-{3217,3124,0,1,0}-{3260,3077,0,1,0}-{3266,3010,0,1,0}-{3267,3068,0,1,0}-{3287,3065,0,1,0}-{3287,3067,0,1,0}-{3310,3070,0,1,0}-{3312,3068,0,1,0}-{3320,3054,0,1,0}-{3324,3013,0,1,0}-{3325,3033,0,1,0}-{3283,3084,0,1,0}-"
}, },
{ {
"npc_id": "6052", "npc_id": "6052",
@ -10419,6 +10427,38 @@
"npc_id": "6117", "npc_id": "6117",
"loc_data": "{1884,5020,0,0,6}-" "loc_data": "{1884,5020,0,0,6}-"
}, },
{
"npc_id": "6118",
"loc_data": "{2443,3190,0,0,6}-"
},
{
"npc_id": "6119",
"loc_data": "{2439,3186,0,1,4}-"
},
{
"npc_id": "6120",
"loc_data": "{2437,3160,1,1,4}-"
},
{
"npc_id": "6122",
"loc_data": "{2327,9394,0,0,1}-"
},
{
"npc_id": "6123",
"loc_data": "{2364,9399,0,0,1}-"
},
{
"npc_id": "6124",
"loc_data": "{2364,9398,0,0,1}-"
},
{
"npc_id": "6125",
"loc_data": "{2323,9377,0,1,0}-"
},
{
"npc_id": "6126",
"loc_data": "{2351,9358,0,1,0}-"
},
{ {
"npc_id": "6127", "npc_id": "6127",
"loc_data": "{2467,3183,0,0,3}-" "loc_data": "{2467,3183,0,0,3}-"
@ -12135,10 +12175,6 @@
"npc_id": "7804", "npc_id": "7804",
"loc_data": "{3279,4350,0,1,0}-{3294,4353,0,1,0}-{3294,4366,0,1,0}-" "loc_data": "{3279,4350,0,1,0}-{3294,4353,0,1,0}-{3294,4366,0,1,0}-"
}, },
{
"npc_id": "7823",
"loc_data": ""
},
{ {
"npc_id": "7891", "npc_id": "7891",
"loc_data": "{3207,3250,0,0,0}-{3208,3250,0,0,0}-{3209,3250,0,0,0}-" "loc_data": "{3207,3250,0,0,0}-{3208,3250,0,0,0}-{3209,3250,0,0,0}-"
@ -12301,7 +12337,7 @@
}, },
{ {
"npc_id": "8324", "npc_id": "8324",
"loc_data": "{2911,3811,0,1,0}-{2911,3812,0,1,0}-{2911,3813,0,1,0}-{2925,3821,0,1,0}-{2925,3822,0,1,0}-{2925,3823,0,1,0}-{2929,3798,0,1,0}-{2936,3790,0,1,0}-{2936,3810,0,1,0}-{2939,3823,0,1,0}-{2949,3819,1,1,0}-{2949,3822,1,1,0}-{2955,3822,1,1,0}-{2957,3822,1,1,0}-{3016,9977,1,1,0}-{3021,9939,1,1,0}-{3024,9954,1,1,0}-{3025,9943,1,1,0}-{3025,9954,1,1,0}-{3026,9966,1,1,0}-{3032,9952,1,1,0}-{3039,9954,1,1,0}-{3044,9967,1,1,0}-{3044,9971,1,1,0}-{3057,9936,1,1,0}-{3059,9953,1,1,0}-{3041,9975,2,1,0}-{3043,9975,2,1,0}-{3045,9975,2,1,0}-{3016,10022,2,1,0}-{3017,10039,2,1,0}-{3027,10028,2,1,0}-{3029,10013,2,1,0}-{3036,10038,2,1,0}-{3037,10006,2,1,0}-{3041,10024,2,1,0}-{3043,10032,2,1,0}-{3057,10002,2,1,0}-{3058,10021,2,1,0}-{3065,10006,2,1,0}-{3027,10092,1,1,0}-{3035,10097,1,1,0}-{3051,10099,1,1,0}-{3427,5102,0,1,0}-{3428,5099,0,1,0}-{3428,5102,0,1,0}-{3430,5099,0,1,0}-" "loc_data": "{2911,3811,0,1,0}-{2911,3812,0,1,0}-{2911,3813,0,1,0}-{2925,3821,0,1,0}-{2925,3822,0,1,0}-{2925,3823,0,1,0}-{2929,3798,0,1,0}-{2936,3790,0,1,0}-{2936,3810,0,1,0}-{2939,3823,0,1,0}-{3427,5102,0,1,0}-{3428,5099,0,1,0}-{3428,5102,0,1,0}-{3430,5099,0,1,0}-{2949,3819,1,1,0}-{2949,3822,1,1,0}-{2955,3822,1,1,0}-{2957,3822,1,1,0}-{3016,9977,1,1,0}-{3021,9939,1,1,0}-{3024,9954,1,1,0}-{3025,9943,1,1,0}-{3025,9954,1,1,0}-{3026,9966,1,1,0}-{3032,9952,1,1,0}-{3039,9954,1,1,0}-{3044,9967,1,1,0}-{3044,9971,1,1,0}-{3057,9936,1,1,0}-{3059,9953,1,1,0}-{3041,9975,2,1,0}-{3043,9975,2,1,0}-{3045,9975,2,1,0}-{3016,10022,2,1,0}-{3017,10039,2,1,0}-{3027,10028,2,1,0}-{3029,10013,2,1,0}-{3036,10038,2,1,0}-{3037,10006,2,1,0}-{3041,10024,2,1,0}-{3043,10032,2,1,0}-{3057,10002,2,1,0}-{3058,10021,2,1,0}-{3065,10006,2,1,0}-{3027,10092,1,1,0}-{3035,10097,1,1,0}-{3051,10099,1,1,0}-"
}, },
{ {
"npc_id": "8328", "npc_id": "8328",

View file

@ -20055,6 +20055,10 @@
"examine": "Home sweet home?", "examine": "Home sweet home?",
"ids": "15480" "ids": "15480"
}, },
{
"examine": "Baby bread.",
"ids": "15506,15507"
},
{ {
"examine": "Home sweet home?", "examine": "Home sweet home?",
"ids": "15748" "ids": "15748"
@ -20183,6 +20187,18 @@
"examine": "A short longboat!", "examine": "A short longboat!",
"ids": "21834" "ids": "21834"
}, },
{
"examine": "A place to sit and watch furniture grow.",
"ids": "28627"
},
{
"examine": "It's not rolling, but it seems to have gathered some moss.",
"ids": "28635"
},
{
"examine": "A thick metal gate.",
"ids": "28690,28691,28692,28693"
},
{ {
"examine": "Contains traces of summoning energy.", "examine": "Contains traces of summoning energy.",
"ids": "29939,29943,29944,29945,29947,29951,29952,29953,29954" "ids": "29939,29943,29944,29945,29947,29951,29952,29953,29954"

View file

@ -20,7 +20,7 @@ import org.rs09.consts.Sounds
import java.util.* import java.util.*
/** /**
* Represents an enchanted jewellery. * Represents a piece of enchanted jewellery.
* @author Vexia, downthecrop, Player Name * @author Vexia, downthecrop, Player Name
*/ */
enum class EnchantedJewellery( enum class EnchantedJewellery(

View file

@ -1,87 +0,0 @@
package content.data;
import core.game.node.item.Item;
/**
* Represents the repair item type.
* @author Vexia
*/
public enum RepairItem {
BRONZE_HATCHET(new Item(494, 1), new Item(1351, 1), 0),
BRONZE_PICKAXE(new Item(468, 1), new Item(1265, 1), 0),
IRON_HATCHET(new Item(496, 1), new Item(1349, 1), 0),
IRON_PICKAXE(new Item(470, 1), new Item(1267, 1), 0),
STEEL_HATCHET(new Item(498, 1), new Item(1353, 1), 0),
STEEL_PICKAXE(new Item(472, 1), new Item(1269, 1), 14),
BLACK_HATCHET(new Item(500, 1), new Item(1361, 1), 10),
MITHRIL_HATCHET(new Item(502, 1), new Item(1355, 1), 18),
MITHRIL_PICKAXE(new Item(474, 1), new Item(1273, 1), 43),
ADAMANT_HATCHET(new Item(504, 1), new Item(1357, 1), 43),
ADAMANT_PICKAXE(new Item(476, 1), new Item(1271, 1), 107),
RUNE_HATCHET(new Item(506, 1), new Item(1359, 1), 427),
RUNE_PICKAXE(new Item(478, 1), new Item(1275, 1), 1100),
DRAGON_HATCHET(new Item(6741, 1), new Item(6739, 1), 1800);
/**
* The item id.
*/
private final Item item;
/**
* The product item.
*/
private final Item product;
/**
* The cost of the money to repair.
*/
private final int cost;
/**
* Constructs a new {@code BobRepairItem} {@code Object}.
* @param item the item.
* @param product the product.
* @param cost the cost.
*/
RepairItem(Item item, Item product, int cost) {
this.item = item;
this.product = product;
this.cost = cost;
}
/**
* Gets the item.
* @return The item.
*/
public Item getItem() {
return item;
}
/**
* Gets the product.
* @return The product.
*/
public Item getProduct() {
return product;
}
/**
* Gets the cost.
* @return The cost.
*/
public int getCost() {
return cost;
}
/**
* Gets the reapir item by the id.
* @param id the id.
* @return the repair item.
*/
public static RepairItem forId(int id) {
for (RepairItem item : RepairItem.values())
if (item.item.getId() == id)
return item;
return null;
}
}

View file

@ -0,0 +1,43 @@
package content.data
import core.game.node.item.Item
/**
* Represents the repair item type.
* @author Vexia
* @author Damighty - Kotlin conversion
*/
enum class RepairItem(
val item: Item,
val product: Item,
val cost: Int
) {
BRONZE_HATCHET(Item(494, 1), Item(1351, 1), 0),
BRONZE_PICKAXE(Item(468, 1), Item(1265, 1), 0),
IRON_HATCHET(Item(496, 1), Item(1349, 1), 0),
IRON_PICKAXE(Item(470, 1), Item(1267, 1), 0),
STEEL_HATCHET(Item(498, 1), Item(1353, 1), 0),
STEEL_PICKAXE(Item(472, 1), Item(1269, 1), 14),
BLACK_HATCHET(Item(500, 1), Item(1361, 1), 10),
MITHRIL_HATCHET(Item(502, 1), Item(1355, 1), 18),
MITHRIL_PICKAXE(Item(474, 1), Item(1273, 1), 43),
ADAMANT_HATCHET(Item(504, 1), Item(1357, 1), 43),
ADAMANT_PICKAXE(Item(476, 1), Item(1271, 1), 107),
RUNE_HATCHET(Item(506, 1), Item(1359, 1), 427),
RUNE_PICKAXE(Item(478, 1), Item(1275, 1), 1100),
DRAGON_HATCHET(Item(6741, 1), Item(6739, 1), 1800);
companion object {
/**
* List of all repairable item IDs.
*/
@JvmStatic
val repairableItemIds: List<Int> = values().map { it.item.id }
/**
* Gets the repair item by the broken items ID.
*/
@JvmStatic
fun forId(id: Int): RepairItem? = values().firstOrNull { it.item.id == id }
}
}

View file

@ -45,35 +45,35 @@ class ChampionChallengeListener : InteractionListener, MapArea {
private val IMP_SCROLL_TEXT = arrayOf( private val IMP_SCROLL_TEXT = arrayOf(
"How about picking on someone your own size? I'll", "How about picking on someone your own size? I'll",
"see you at the Champion's Guild.", "see you at the Champions' Guild.",
"", "",
"Champion of Imps" "Champion of Imps"
) )
private val GOBLIN_SCROLL_TEXT = arrayOf( private val GOBLIN_SCROLL_TEXT = arrayOf(
"Fight me if you think you can human, I'll wait", "Fight me if you think you can human, I'll wait",
"for you in the Champion's Guild.", "for you in the Champions' Guild.",
"", "",
"Champion of Goblins" "Champion of Goblins"
) )
private val SKELETON_SCROLL_TEXT = arrayOf( private val SKELETON_SCROLL_TEXT = arrayOf(
"I'll be waiting at the Champions' Guild to", "I'll be waiting at the Champions' Guild to collect",
"collect your bones.", "your bones.",
"", "",
"Champion of Skeletons" "Champion of Skeletons"
) )
private val ZOMBIE_SCROLL_TEXT = arrayOf( private val ZOMBIE_SCROLL_TEXT = arrayOf(
"You come to Champions' Guild, you fight me,", "You come to Champions' Guild, you fight me, I",
"I squish you, I get brains!", "squish you, I get brains!",
"", "",
"Champion of Zombies" "Champion of Zombies"
) )
private val GIANT_SCROLL_TEXT = arrayOf( private val GIANT_SCROLL_TEXT = arrayOf(
"Get yourself to the Champions' Guild, if you", "Get yourself to the Champions' Guild, if you dare",
"dare to face me puny human.", "to face me puny human.",
"", "",
"Champion of Giants" "Champion of Giants"
) )
@ -93,21 +93,22 @@ class ChampionChallengeListener : InteractionListener, MapArea {
) )
private val EARTH_WARRIOR_TEXT = arrayOf( private val EARTH_WARRIOR_TEXT = arrayOf(
"I challenge you to a duel, come to the arena beneath", "I challenge you to a duel, come to the arena",
"the Champion's Guild and fight me if you dare.", "beneath the Champions' Guild and fight me if you",
"dare.",
"", "",
"Champion of Earth Warriors" "Champion of Earth Warriors"
) )
private val JOGRE_SCROLL_TEXT = arrayOf( private val JOGRE_SCROLL_TEXT = arrayOf(
"You think you can defeat me? Come to the", "You think you can defeat me? Come to the",
"Champion's Guild and prove it!", "Champions' Guild and prove it!",
"", "",
"Champion of Jogres" "Champion of Jogres"
) )
private val LESSER_DEMON_SCROLL_TEXT = arrayOf( private val LESSER_DEMON_SCROLL_TEXT = arrayOf(
"Come to the Champion's Guild so I can banish", "Come to the Champions' Guild so I can banish",
"you mortal!", "you mortal!",
"", "",
"Champion of Lesser Demons" "Champion of Lesser Demons"

View file

@ -70,7 +70,7 @@ class ChampionScrollsDropHandler : ChampionScrollsEventHookBase() {
NPCs.CAVE_GOBLIN_GUARD_2073, NPCs.CAVE_GOBLIN_GUARD_2074, NPCs.CAVE_GOBLIN_GUARD_2073, NPCs.CAVE_GOBLIN_GUARD_2074,
NPCs.GOBLIN_GUARD_489, NPCs.GOBLIN_GUARD_6496, NPCs.GOBLIN_GUARD_6497, NPCs.GOBLIN_GUARD_6496, NPCs.GOBLIN_GUARD_6497,
NPCs.SERGEANT_GRIMSPIKE_6265, NPCs.SERGEANT_GRIMSPIKE_6265,
NPCs.SERGEANT_STEELWILL_6263, NPCs.SERGEANT_STEELWILL_6263,
NPCs.SERGEANT_STRONGSTACK_6261 NPCs.SERGEANT_STRONGSTACK_6261

View file

@ -27,24 +27,24 @@ class LarxusDialogue(val ChallengeStart: Boolean = false) : DialogueFile() {
0 -> { 0 -> {
face(findNPC(NPCs.LARXUS_3050)!!, player!!, 1) face(findNPC(NPCs.LARXUS_3050)!!, player!!, 1)
for (i in scrolls)when{ for (i in scrolls)when{
inInventory(player!!,Items.CHAMPION_SCROLL_6798) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use prayer's. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6798) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Prayers. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6799) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're allowed to use only weapons. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6799) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're only allowed to take Weapons, no other items are allowed. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6800) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're allowed to use only melee combat skill. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6800) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're only allowed to use Melee attacks, no Ranged or Magic. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6801) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're allowed to use only magic skill. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6801) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're only allowed to use Magic attacks, no Melee or Ranged. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6802) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use melee combat skills. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6802) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Melee attacks. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6803) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use weapons with special attack. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6803) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Special Attacks. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6804) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use ranged skill. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6804) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Ranged attacks. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6805) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're allowed to use only equipment. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6805) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed any Weapons or Armour. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6806) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're allowed to use only ranged skill. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6806) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're only allowed to use Ranged attacks, no Melee or Magic. Do you still want to proceed?").also { stage = 1 }
inInventory(player!!,Items.CHAMPION_SCROLL_6807) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use magic skill. Do you still want to proceed?").also { stage = 1 } inInventory(player!!,Items.CHAMPION_SCROLL_6807) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Magic attacks. Do you still want to proceed?").also { stage = 1 }
else -> { else -> {
sendMessage(player!!, "Nothing interesting happens.").also { stage = END_DIALOGUE } } sendMessage(player!!, "Nothing interesting happens.").also { stage = END_DIALOGUE } }
} }
} }
1 -> options("Yes, let me at him!", "No, thanks I'll pass.").also { stage = 2 } 1 -> options("Yes, let me at him!", "No thanks, I'll pass.").also { stage = 2 }
2 -> when (buttonID) { 2 -> when (buttonID) {
1 -> playerl("Yes, let me at him!").also { stage = 3 } 1 -> playerl("Yes, let me at him!").also { stage = 3 }
2 -> playerl("No, thanks I'll pass.").also { stage = END_DIALOGUE } 2 -> playerl("No thanks, I'll pass.").also { stage = END_DIALOGUE }
} }
3 -> npcl("Your challenger is ready, please go down through the trapdoor when you're ready.").also { stage = 4 } 3 -> npcl("Your challenger is ready, please go down through the trapdoor when you're ready.").also { stage = 4 }
4 -> { 4 -> {
@ -65,7 +65,7 @@ class LarxusDialogue(val ChallengeStart: Boolean = false) : DialogueFile() {
3 -> playerl("Nothing thanks.").also { stage = END_DIALOGUE } 3 -> playerl("Nothing thanks.").also { stage = END_DIALOGUE }
} }
3 -> npcl("Well pass it here and we'll get you started.").also { stage = END_DIALOGUE } 3 -> npcl("Well pass it here and we'll get you started.").also { stage = END_DIALOGUE }
4 -> npcl("This is the champions' arena, the champions of various, races use it to duel those they deem worthy of the, honour.").also { stage = END_DIALOGUE } 4 -> npcl("This is the champions' arena. The champions of various races use it to duel those they deem worthy of the honour. If you find a challenge scroll in your travels, bring it here to face your challenger.").also { stage = END_DIALOGUE }
} }
} }
} }

View file

@ -3,8 +3,6 @@ package content.global.activity.cchallange.npc
import core.api.* import core.api.*
import core.game.node.entity.Entity import core.game.node.entity.Entity
import core.game.node.entity.combat.BattleState import core.game.node.entity.combat.BattleState
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.combat.equipment.WeaponInterface
import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.npc.AbstractNPC
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
@ -63,21 +61,11 @@ class ImpChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,
super.checkImpact(state) super.checkImpact(state)
val player = state.attacker val player = state.attacker
if (player is Player) { if (player is Player) {
val w = player.getExtension<WeaponInterface>(WeaponInterface::class.java)
if (state.style == CombatStyle.MELEE || state.style == CombatStyle.MAGIC || state.style == CombatStyle.RANGE) { //somehow the maximumHit is determined to be zero by this point if you're using a melee special attack.
if (state.maximumHit == 0) {
state.neutralizeHits() state.neutralizeHits()
state.estimatedHit = state.maximumHit sendMessage(player, "Larxus said you couldn't use special attacks in this duel.")
}
if (w.weaponInterface.interfaceId == 10) {
sendMessage(player, "You cannot use special attack in this challenge.")
if (state.estimatedHit > -1) {
state.estimatedHit = 0
return
}
if (state.secondaryHit > -1) {
state.secondaryHit = 0
return
}
} }
} }
} }

View file

@ -6,6 +6,7 @@ import core.game.node.entity.player.Player
import core.game.node.entity.skill.SkillPulse import core.game.node.entity.skill.SkillPulse
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import content.data.skill.SkillingTool import content.data.skill.SkillingTool
import core.ServerConstants
import core.game.node.item.Item import core.game.node.item.Item
import core.tools.RandomFunction import core.tools.RandomFunction
import org.rs09.consts.Items import org.rs09.consts.Items
@ -13,6 +14,9 @@ import core.game.world.GameWorld
import core.game.world.repository.Repository import core.game.world.repository.Repository
import core.tools.colorize import core.tools.colorize
// TODO: Shooting stars should roll for bonus gems while mining
// See: https://youtu.be/6OqZ2TGc6fM?si=U8nB5IDQREhWXApD
/** /**
* The pulse used to handle mining shooting stars. * The pulse used to handle mining shooting stars.
*/ */
@ -53,7 +57,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
ShootingStarPlugin.getStoreFile()["isDiscovered"] = star.isDiscovered ShootingStarPlugin.getStoreFile()["isDiscovered"] = star.isDiscovered
return player.skills.getLevel(Skills.MINING) >= star.miningLevel return player.skills.getLevel(Skills.MINING) >= star.miningLevel
} }
if (player.skills.getLevel(Skills.MINING) < star.miningLevel) { if (player.skills.getLevel(Skills.MINING) < star.miningLevel) {
player.dialogueInterpreter.sendDialogue("You need a Mining level of at least " + star.miningLevel + " in order to mine this layer.") player.dialogueInterpreter.sendDialogue("You need a Mining level of at least " + star.miningLevel + " in order to mine this layer.")
return false return false
@ -100,7 +104,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
if (ShootingStarPlugin.getStarDust(player) < 200) { if (ShootingStarPlugin.getStarDust(player) < 200) {
player.inventory.add(Item(ShootingStarPlugin.STAR_DUST, 1)) player.inventory.add(Item(ShootingStarPlugin.STAR_DUST, 1))
} }
if(!inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !inBank(player, Items.ANCIENT_BLUEPRINT_14651)){ if (ServerConstants.SHOOTING_STAR_RING && hasAnItem(player, Items.ANCIENT_BLUEPRINT_14651).container == null) {
rollBlueprint(player) rollBlueprint(player)
} }
@ -130,7 +134,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
override fun message(type: Int) { override fun message(type: Int) {
when (type) { when (type) {
0 -> player.packetDispatch.sendMessage("You swing your pickaxe at the rock...") 0 -> player.packetDispatch.sendMessage("You swing your pickaxe at the rock.")
} }
} }

View file

@ -2,22 +2,57 @@ package content.global.ame
import core.ServerConstants import core.ServerConstants
import core.api.* import core.api.*
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager.TeleportType import core.game.node.entity.player.link.TeleportManager.TeleportType
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Sounds
fun kidnapPlayer(player: Player, loc: Location, type: TeleportType) { fun kidnapPlayer(npc: NPC, player: Player, dest: Location, playerLine: String? = null, callback: (player: Player, npc: NPC) -> Unit) {
setAttribute(player, "kidnapped-by-random", true) //only used in POH code when you leave the hut, so does not need /save. Do not rely on this outside of its intended POH use case. val lockDuration = if (playerLine != null) 4 else 6
if (getAttribute(player, "/save:original-loc", null) == null) { lock(player, lockDuration)
setAttribute(player, "/save:original-loc", player.location) queueScript(player, 1, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
if (playerLine != null) {
sendChat(player, playerLine)
return@queueScript delayScript(player, 2)
}
return@queueScript delayScript(player, 0)
}
1 -> {
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
2 -> {
setAttribute(player, "kidnapped-by-random", true)
if (getAttribute<Location?>(player, "/save:original-loc", null) == null) {
setAttribute(player, "/save:original-loc", player.location)
}
teleport(player, dest, TeleportType.INSTANT)
sendGraphics(Graphics(1577, 0, 0), player.location)
animate(player, 8941)
resetAnimator(player)
callback(player, npc)
return@queueScript delayScript(player, 2)
}
3 -> {
removeAttribute(player, "kidnapped-by-random") //this is not needed at this point anymore and will reenable the original-loc sanity check tick action
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
teleport(player, loc, type)
} }
fun returnPlayer(player: Player) { fun returnPlayer(player: Player) {
player.locks.unlockTeleport() player.locks.unlockTeleport()
val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION ?: Location.create(3222, 3218, 0)) val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION)
teleport(player, destination) teleport(player, destination!!)
unlock(player) unlock(player)
removeAttributes(player, "/save:original-loc", "kidnapped-by-random") removeAttributes(player, "/save:original-loc", "kidnapped-by-random")
} }

View file

@ -4,6 +4,7 @@ import content.global.ame.events.surpriseexam.MysteriousOldManNPC
import core.api.playGlobalAudio import core.api.playGlobalAudio
import core.api.poofClear import core.api.poofClear
import core.api.sendMessage import core.api.sendMessage
import core.api.setAttribute
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.MovementPulse import core.game.interaction.MovementPulse
import core.game.node.entity.Entity import core.game.node.entity.Entity
@ -19,8 +20,10 @@ import core.game.world.map.RegionManager
import core.game.world.map.path.Pathfinder import core.game.world.map.path.Pathfinder
import core.game.world.update.flag.context.Graphics import core.game.world.update.flag.context.Graphics
import core.integrations.discord.Discord import core.integrations.discord.Discord
import core.tools.RandomFunction
import core.tools.secondsToTicks import core.tools.secondsToTicks
import core.tools.ticksToCycles import core.tools.ticksToCycles
import org.rs09.consts.NPCs
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.min import kotlin.math.min
@ -43,6 +46,7 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
event.loot = loot event.loot = loot
event.player = player event.player = player
event.spawnLocation = RegionManager.getSpawnLocation(player, this) event.spawnLocation = RegionManager.getSpawnLocation(player, this)
setAttribute(event, "spawned-by-ame", true)
return event return event
} }
@ -101,7 +105,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
} }
open fun onTimeUp() { open fun onTimeUp() {
noteAndTeleport()
terminate() terminate()
} }
@ -144,4 +147,23 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
val index = min(ids.size, ceil(player.properties.currentCombatLevel / 20.0).toInt()) - 1 val index = min(ids.size, ceil(player.properties.currentCombatLevel / 20.0).toInt()) - 1
return ids[index] return ids[index]
} }
fun sayLine(npc: NPC, phrases: Array<String>, hasOpeningPhrase: Boolean, hasOverTimePhrase: Boolean) {
if (!timerPaused && (ticksLeft % 20 == 0 || ticksLeft <= 2)) { //unless the Certer interface is up, speak every 20 ticks, or in the 2nd-to-last tick before attack/note-&-teleport
var playDwarfWhistle = true
if (ticksLeft == secondsToTicks(180) && hasOpeningPhrase) {
sendChat(phrases[0])
} else if (ticksLeft <= 2 && hasOverTimePhrase) {
sendChat(phrases[phrases.size - 1])
playDwarfWhistle = false
} else {
val start = if (hasOpeningPhrase) 0 else 1
val end = if (hasOverTimePhrase) phrases.size - 2 else phrases.size - 1
sendChat(phrases[RandomFunction.random(start, end + 1)])
}
if (npc.id == NPCs.DRUNKEN_DWARF_956 && playDwarfWhistle) {
playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297)
}
}
}
} }

View file

@ -97,5 +97,4 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n
} }
} }
} }
} }

View file

@ -1,5 +1,6 @@
package content.global.ame.events package content.global.ame.events
import core.api.getAttribute
import core.game.node.entity.Entity import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior import core.game.node.entity.npc.NPCBehavior
@ -8,12 +9,12 @@ import org.rs09.consts.NPCs
class HostileRandomEventBehavior : NPCBehavior( class HostileRandomEventBehavior : NPCBehavior(
NPCs.EVIL_CHICKEN_2463, NPCs.EVIL_CHICKEN_2464, NPCs.EVIL_CHICKEN_2465, NPCs.EVIL_CHICKEN_2466, NPCs.EVIL_CHICKEN_2467, NPCs.EVIL_CHICKEN_2468, NPCs.EVIL_CHICKEN_2463, NPCs.EVIL_CHICKEN_2464, NPCs.EVIL_CHICKEN_2465, NPCs.EVIL_CHICKEN_2466, NPCs.EVIL_CHICKEN_2467, NPCs.EVIL_CHICKEN_2468,
NPCs.RIVER_TROLL_391, NPCs.RIVER_TROLL_392, NPCs.RIVER_TROLL_393, NPCs.RIVER_TROLL_394, NPCs.RIVER_TROLL_395, NPCs.RIVER_TROLL_396, NPCs.RIVER_TROLL_391, NPCs.RIVER_TROLL_392, NPCs.RIVER_TROLL_393, NPCs.RIVER_TROLL_394, NPCs.RIVER_TROLL_395, NPCs.RIVER_TROLL_396,
NPCs.ROCK_GOLEM_413, NPCs.ROCK_GOLEM_414, NPCs.ROCK_GOLEM_415, NPCs.ROCK_GOLEM_416, NPCs.ROCK_GOLEM_417, NPCs.ROCK_GOLEM_418,
NPCs.SHADE_425, NPCs.SHADE_426, NPCs.SHADE_427, NPCs.SHADE_428, NPCs.SHADE_429, NPCs.SHADE_430, NPCs.SHADE_431, NPCs.SHADE_425, NPCs.SHADE_426, NPCs.SHADE_427, NPCs.SHADE_428, NPCs.SHADE_429, NPCs.SHADE_430, NPCs.SHADE_431,
NPCs.TREE_SPIRIT_438, NPCs.TREE_SPIRIT_439, NPCs.TREE_SPIRIT_440, NPCs.TREE_SPIRIT_441, NPCs.TREE_SPIRIT_442, NPCs.TREE_SPIRIT_443, NPCs.TREE_SPIRIT_438, NPCs.TREE_SPIRIT_439, NPCs.TREE_SPIRIT_440, NPCs.TREE_SPIRIT_441, NPCs.TREE_SPIRIT_442, NPCs.TREE_SPIRIT_443,
NPCs.ZOMBIE_419, NPCs.ZOMBIE_420, NPCs.ZOMBIE_421, NPCs.ZOMBIE_422, NPCs.ZOMBIE_423, NPCs.ZOMBIE_424 NPCs.ZOMBIE_419, NPCs.ZOMBIE_420, NPCs.ZOMBIE_421, NPCs.ZOMBIE_422, NPCs.ZOMBIE_423, NPCs.ZOMBIE_424
) { ) {
override fun getXpMultiplier(self: NPC, attacker: Entity): Double { override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return super.getXpMultiplier(self, attacker) / 16.0 val xprate = super.getXpMultiplier(self, attacker)
return if (getAttribute(self, "spawned-by-ame", false)) xprate / 16.0 else xprate
} }
} }

View file

@ -4,42 +4,19 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.TeleportManager
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
/** "::revent -p player_name -e candlelight" **/
class PiousPeteNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PRIEST_3206) { class PiousPeteNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PRIEST_3206) {
override fun init() { override fun init() {
super.init() super.init()
// Supposed to be "I'm sorry to drag you away from your tasks, but I need a little help with something." but it's too goddamn long. // Supposed to be "I'm sorry to drag you away from your tasks, but I need a little help with something." but it's too goddamn long.
sendChat("${player.username.capitalize()}! I need a little help with something.") sendChat("${player.username}! I need a little help with something.")
face(player) face(player)
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> kidnapPlayer(this, player, Location(1972, 5002, 0)) { player, _ ->
when (stage) { CandlelightInterface.initCandlelight(player)
0 -> { openDialogue(player, PiousPeteStartingDialogueFile(), NPC(NPCs.PIOUS_PETE_3207))
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 -> {
CandlelightInterface.initCandlelight(player)
kidnapPlayer(player, Location(1972, 5002, 0), TeleportManager.TeleportType.INSTANT)
// AntiMacro.terminateEventNpc(player)
sendGraphics(Graphics(1577, 0, 0), player.location)
animate(player,8941)
openDialogue(player, PiousPeteStartingDialogueFile(), NPC(NPCs.PIOUS_PETE_3207))
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
} }

View file

@ -2,29 +2,19 @@ package content.global.ame.events.certer
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.emote.Emotes import core.game.node.entity.player.link.emote.Emotes
import core.tools.RandomFunction
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import core.api.animate import core.api.animate
import core.api.lock
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GILES_2538) { class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GILES_2538) {
lateinit var pName: String
lateinit var phrases: Array<String> lateinit var phrases: Array<String>
override fun tick() { override fun tick() {
// Don't speak if we have the interface opened sayLine(this, phrases, true, true)
if (!timerPaused) { if (ticksLeft == 2) {
// Over allotted time phrase lock(player, 2)
if (ticksLeft <= 2) {
player.lock(2)
sendChat(phrases[4])
// Say a phrase every 20 ticks starting at 280 ticks
// as to not interfere with the init chat phrase
} else if (ticksLeft <= 280 && ticksLeft % 20 == 0) {
sendChat(phrases[RandomFunction.random(1, 3)])
}
} }
super.tick() super.tick()
} }
@ -36,15 +26,20 @@ class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NP
override fun init() { override fun init() {
super.init() super.init()
pName = player.username.capitalize() phrases = arrayOf(
phrases = arrayOf("Greetings $pName, I need your help.", "Greetings ${player.username}, I need your help.",
"ehem... Hello $pName, please talk to me!", "ehem... Hello ${player.username}, please talk to me!",
"Hello, are you there $pName?", "Hello, are you there ${player.username}?",
"It's really rude to ignore someone, $pName!", "It's really rude to ignore someone, ${player.username}!",
"No-one ignores me!") "No-one ignores me!"
)
player.setAttribute("random:pause", false) player.setAttribute("random:pause", false)
player.setAttribute("certer:reward", false) player.setAttribute("certer:reward", false)
sendChat(phrases[0])
animate(this, Emotes.BOW.animation, true) animate(this, Emotes.BOW.animation, true)
} }
override fun onTimeUp() {
noteAndTeleport()
terminate()
}
} }

View file

@ -1,12 +1,9 @@
package content.global.ame.events.drilldemon package content.global.ame.events.drilldemon
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer import content.global.ame.returnPlayer
import core.api.* import core.api.*
import core.game.interaction.QueueStrength import core.game.interaction.QueueStrength
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneBorders
import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items import org.rs09.consts.Items
@ -24,13 +21,6 @@ object DrillDemonUtils {
val DD_AREA = ZoneBorders(3158, 4817, 3168, 4823) val DD_AREA = ZoneBorders(3158, 4817, 3168, 4823)
val DD_NPC = NPCs.SERGEANT_DAMIEN_2790 val DD_NPC = NPCs.SERGEANT_DAMIEN_2790
fun teleport(player: Player) {
kidnapPlayer(player, Location.create(3163, 4819, 0), TeleportManager.TeleportType.INSTANT)
player.interfaceManager.closeDefaultTabs()
setComponentVisibility(player, 548, 69, true)
setComponentVisibility(player, 746, 12, true)
}
fun changeSignsAndAssignTask(player: Player) { fun changeSignsAndAssignTask(player: Player) {
setVarp(player, DD_SIGN_VARP, 0) setVarp(player, DD_SIGN_VARP, 0)
val tempList = arrayListOf(DD_SIGN_JOG, DD_SIGN_JUMP, DD_SIGN_PUSHUP, DD_SIGN_SITUP).shuffled().toMutableList() val tempList = arrayListOf(DD_SIGN_JOG, DD_SIGN_JUMP, DD_SIGN_PUSHUP, DD_SIGN_SITUP).shuffled().toMutableList()
@ -94,6 +84,5 @@ object DrillDemonUtils {
} }
return@queueScript stopExecuting(player) return@queueScript stopExecuting(player)
} }
} }
} }

View file

@ -3,32 +3,22 @@ package content.global.ame.events.drilldemon
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength import core.game.world.map.Location
import core.game.system.timer.impl.AntiMacro
import core.tools.secondsToTicks
class SergeantDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) { class SergeantDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) {
override fun init() { override fun init() {
super.init() super.init()
sendChat(player.username+ "! Drop and give me 20!") sendChat("${player.username}! Drop and give me 20!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(3163, 4819, 0)) { player, _ ->
0 -> { player.interfaceManager.closeDefaultTabs()
lock(player, secondsToTicks(30)) setComponentVisibility(player, 548, 69, true)
DrillDemonUtils.teleport(player) setComponentVisibility(player, 746, 12, true)
AntiMacro.terminateEventNpc(player) openDialogue(player, SeargentDamienDialogue(isCorrect = true, eventStart = true), NPCs.SERGEANT_DAMIEN_2790)
return@queueScript delayScript(player, 2)
}
1 -> {
openDialogue(player, SeargentDamienDialogue(isCorrect = true, eventStart = true), NPCs.SERGEANT_DAMIEN_2790)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
} }

View file

@ -9,35 +9,27 @@ import org.rs09.consts.NPCs
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
class DrunkenDwarfNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.DRUNKEN_DWARF_956) { class DrunkenDwarfNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.DRUNKEN_DWARF_956) {
private val phrases = arrayOf("Oi, are you der @name!","Dunt ignore your matey!","Aww comeon, talk to ikle me @name!") lateinit var phrases: Array<String>
private var attackPhrase = false
private var attackDelay = 0 private var attackDelay = 0
private var lastPhraseTime = 0
private fun sendPhrases() {
if (getWorldTicks() > lastPhraseTime + 5) {
playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297)
sendChat(this, phrases.random().replace("@name",player.username.capitalize()))
this.face(player)
lastPhraseTime = getWorldTicks()
}
}
override fun init() { override fun init() {
super.init() super.init()
playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297) phrases = arrayOf(
sendChat(this, "'Ello der ${player.username.capitalize()}! *hic*") "'Ello der ${player.username}! *hic*",
"Oi, are you der ${player.username}!",
"Dunt ignore your matey!",
"Aww comeon, talk to ikle me ${player.username}!",
"I hates you, ${player.username}!"
)
} }
override fun tick() { override fun tick() {
if (RandomFunction.roll(20) && !attackPhrase) sayLine(this, phrases, true, true)
sendPhrases()
if (ticksLeft <= 10) { if (ticksLeft <= 10) {
ticksLeft = 10 ticksLeft = 10
if (!attackPhrase) if (attackDelay <= getWorldTicks()) {
sendChat("I hates you, ${player.username.capitalize()}!").also { attackPhrase = true }
if (attackDelay <= getWorldTicks())
this.attack(player) this.attack(player)
}
} }
super.tick() super.tick()
} }
@ -47,4 +39,10 @@ class DrunkenDwarfNPC(override var loot: WeightBasedTable? = null) : RandomEvent
this.pulseManager.clear() this.pulseManager.clear()
openDialogue(player, DrunkenDwarfDialogue(), this.asNpc()) openDialogue(player, DrunkenDwarfDialogue(), this.asNpc())
} }
override fun onTimeUp() {
if (attackDelay <= getWorldTicks()) {
this.attack(player)
}
}
} }

View file

@ -1,40 +1,22 @@
package content.global.ame.events.evilbob package content.global.ame.events.evilbob
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.system.timer.impl.AntiMacro import core.game.world.map.Location
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
class EvilBobNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.EVIL_BOB_2478) { class EvilBobNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.EVIL_BOB_2478) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("meow") sendChat("meow")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(3419, 4776, 0), "No... what? Nooooooooooooo!") { player, _ ->
0 -> { EvilBobUtils.giveEventFishingSpot(player)
lock(player, 6) sendMessage(player, "Welcome to Scape2009.")
sendChat(player, "No... what? Nooooooooooooo!") openDialogue(player, EvilBobDialogue(), NPCs.EVIL_BOB_2479)
animate(player, EvilBobUtils.teleAnim)
player.graphics(EvilBobUtils.telegfx)
playAudio(player, Sounds.TELEPORT_ALL_200)
EvilBobUtils.giveEventFishingSpot(player)
return@queueScript delayScript(player, 3)
}
1 -> {
sendMessage(player, "Welcome to Scape2009.")
EvilBobUtils.teleport(player)
resetAnimator(player)
openDialogue(player, EvilBobDialogue(), NPCs.EVIL_BOB_2479)
AntiMacro.terminateEventNpc(player)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
} }

View file

@ -1,12 +1,8 @@
package content.global.ame.events.evilbob package content.global.ame.events.evilbob
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.* import core.api.*
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneBorders
import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.Graphics import core.game.world.update.flag.context.Graphics
@ -53,10 +49,6 @@ object EvilBobUtils {
} }
} }
fun teleport(player: Player) {
kidnapPlayer(player, Location.create(3419, 4776, 0), TeleportManager.TeleportType.INSTANT)
}
fun cleanup(player: Player) { fun cleanup(player: Player) {
removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen) removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen)
removeAll(player, Items.FISHLIKE_THING_6202) removeAll(player, Items.FISHLIKE_THING_6202)

View file

@ -3,38 +3,47 @@ package content.global.ame.events.evilbob
import core.api.* import core.api.*
import core.game.activity.Cutscene import core.game.activity.Cutscene
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.world.map.Direction
class ServantCutsceneN(player: Player) : Cutscene(player) { class ServantCutsceneN(player: Player) : Cutscene(player) {
override fun setup() { override fun setup() {
setExit(player.location.transform(0, 0, 0)) setExit(player.location.transform(0, 0, 0))
loadRegion(13642) loadRegion(13642)
addNPC(2479, 28, 41, Direction.SOUTH) // Evil Bob
addNPC(2481, 31, 41, Direction.SOUTH_WEST) // Servant
} }
override fun runStage(stage: Int) { override fun runStage(stage: Int) {
when (stage) { when (stage) {
0 -> { 0 -> {
fadeToBlack() teleport(player, 30, 41)
timedUpdate(4) moveCamera(30, 37, 400, 255)
rotateCamera(30, 50, 400, 255)
openInterface(player, 186)
timedUpdate(0)
} }
1 -> { 1 -> {
teleport(player, 29, 41) timedUpdate(0)
moveCamera(30, 43) // +7 from statue }
openInterface(player, 186) 2 -> { // Slow start
moveCamera(30, 49, 400, 2)
timedUpdate(2) timedUpdate(2)
} }
2 -> { 3 -> { // Fast middle
moveCamera(30, 44, 380, 4)
timedUpdate(6)
}
4 -> { // Slow end
moveCamera(30, 45, 340, 2)
timedUpdate(2) timedUpdate(2)
rotateCamera(30, 51, 300, 100) // the statue loc
fadeFromBlack()
} }
3 -> { 5 -> {
timedUpdate(9) timedUpdate(2)
moveCamera(30, 46, 300, 2) // +4 from statue
} }
4 -> { 6 -> {
end{ player.locks.lockTeleport(1000000) }
closeInterface(player) closeInterface(player)
end(fade = false) { player.locks.lockTeleport(1000000) }
} }
} }
} }
@ -45,32 +54,40 @@ class ServantCutsceneS(player: Player) : Cutscene(player) {
override fun setup() { override fun setup() {
setExit(player.location.transform(0, 0, 0)) setExit(player.location.transform(0, 0, 0))
loadRegion(13642) loadRegion(13642)
addNPC(2479, 28, 41, Direction.SOUTH) // Evil Bob
addNPC(2481, 31, 41, Direction.SOUTH_WEST) // Servant
} }
override fun runStage(stage: Int) { override fun runStage(stage: Int) {
when (stage) { when (stage) {
0 -> { 0 -> {
fadeToBlack() teleport(player, 30, 41)
timedUpdate(4) moveCamera(31, 46, 365, 255)
rotateCamera(29, 30, 365, 255)
openInterface(player, 186)
timedUpdate(0)
} }
1 -> { 1 -> {
teleport(player, 29, 41) timedUpdate(0)
moveCamera(29, 38) // +7 from statue
openInterface(player, 186)
timedUpdate(2)
} }
2 -> { 2 -> {
moveCamera(31, 43, 480, 2)
timedUpdate(2) timedUpdate(2)
rotateCamera(29, 30, 300, 100) // the statue loc
fadeFromBlack()
} }
3 -> { 3 -> {
timedUpdate(9) moveCamera(31, 37, 455, 4)
moveCamera(29, 35, 300, 2) // +4 from statue timedUpdate(8)
} }
4 -> { 4 -> {
end{ player.locks.lockTeleport(1000000) } moveCamera(31, 36, 395, 2)
timedUpdate(2)
}
5 -> {
timedUpdate(3)
}
6 -> {
closeInterface(player) closeInterface(player)
end(fade = false) { player.locks.lockTeleport(1000000) }
} }
} }
} }
@ -81,32 +98,40 @@ class ServantCutsceneE(player: Player) : Cutscene(player) {
override fun setup() { override fun setup() {
setExit(player.location.transform(0, 0, 0)) setExit(player.location.transform(0, 0, 0))
loadRegion(13642) loadRegion(13642)
addNPC(2479, 28, 41, Direction.SOUTH) // Evil Bob
addNPC(2481, 31, 41, Direction.SOUTH_WEST) // Servant
} }
override fun runStage(stage: Int) { override fun runStage(stage: Int) {
when (stage) { when (stage) {
0 -> { 0 -> {
fadeToBlack() teleport(player, 30, 41)
timedUpdate(4) moveCamera(25, 41, 440, 255)
rotateCamera(42, 41, 440, 255)
openInterface(player, 186)
timedUpdate(0)
} }
1 -> { 1 -> {
teleport(player, 29, 41) timedUpdate(0)
moveCamera(35, 41) // +7 from statue }
openInterface(player, 186) 2 -> { // Slow start
moveCamera(28, 41, 500, 3)
timedUpdate(2) timedUpdate(2)
} }
2 -> { 3 -> { // Fast middle
moveCamera(34, 41, 390, 5)
timedUpdate(6)
}
4 -> { // Slow end
moveCamera(36, 41, 340, 2)
timedUpdate(2) timedUpdate(2)
rotateCamera(43, 41, 300, 100) // the statue loc
fadeFromBlack()
} }
3 -> { 5 -> {
timedUpdate(9) timedUpdate(4)
moveCamera(38, 41, 300, 2) // +4 from statue
} }
4 -> { 6 -> {
end{ player.locks.lockTeleport(1000000) }
closeInterface(player) closeInterface(player)
end(fade = false) { player.locks.lockTeleport(1000000) }
} }
} }
} }
@ -117,34 +142,41 @@ class ServantCutsceneW(player: Player) : Cutscene(player) {
override fun setup() { override fun setup() {
setExit(player.location.transform(0, 0, 0)) setExit(player.location.transform(0, 0, 0))
loadRegion(13642) loadRegion(13642)
addNPC(2479, 28, 41, Direction.SOUTH) // Evil Bob
addNPC(2481, 31, 41, Direction.SOUTH_WEST) // Servant
} }
override fun runStage(stage: Int) { override fun runStage(stage: Int) {
when (stage) { when (stage) {
0 -> { 0 -> {
fadeToBlack() teleport(player, 30, 41)
timedUpdate(4) moveCamera(34, 41, 325, 255)
rotateCamera(16, 40, 300, 255)
openInterface(player, 186)
timedUpdate(0)
} }
1 -> { 1 -> {
teleport(player, 29, 41) timedUpdate(0)
moveCamera(25, 40) // +7 from statue }
openInterface(player, 186) 2 -> { // Slow start
moveCamera(31, 41, 440, 3)
timedUpdate(2) timedUpdate(2)
} }
2 -> { 3 -> { // Fast middle
moveCamera(24, 41, 330, 5)
timedUpdate(7)
}
4 -> { // Slow end
moveCamera(23, 41, 300, 2)
timedUpdate(2) timedUpdate(2)
rotateCamera(18, 40, 300, 100) // the statue loc
fadeFromBlack()
} }
3 -> { 5 -> {
timedUpdate(9) timedUpdate(3)
moveCamera(22, 40, 300, 2) // +4 from statue
} }
4 -> { 6 -> {
end{ player.locks.lockTeleport(1000000) }
closeInterface(player) closeInterface(player)
end(fade = false) { player.locks.lockTeleport(1000000) }
} }
} }
} }
} }

View file

@ -1,13 +1,10 @@
package content.global.ame.events.freakyforester package content.global.ame.events.freakyforester
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer import content.global.ame.returnPlayer
import core.api.* import core.api.*
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneBorders
import core.tools.RandomFunction import core.tools.RandomFunction
@ -28,10 +25,6 @@ object FreakUtils{
player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc) player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc)
} }
fun teleport(player: Player) {
kidnapPlayer(player, Location.create(2599, 4777 ,0), TeleportManager.TeleportType.INSTANT)
}
fun cleanup(player: Player) { fun cleanup(player: Player) {
returnPlayer(player) returnPlayer(player)
removeAttributes(player, freakTask, freakComplete, pheasantKilled) removeAttributes(player, freakTask, freakComplete, pheasantKilled)

View file

@ -1,39 +1,21 @@
package content.global.ame.events.freakyforester package content.global.ame.events.freakyforester
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC 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.Sounds
class FreakyForesterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.FREAKY_FORESTER_2458) { class FreakyForesterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.FREAKY_FORESTER_2458) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("Ah, ${player.username}, just the person I need!") sendChat("Ah, ${player.username}, just the person I need!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(2599, 4777, 0)) { player, _ ->
0 -> { FreakUtils.giveFreakTask(player)
lock(player, 6) openDialogue(player, FreakyForesterDialogue(), FreakUtils.freakNpc)
sendGraphics(Graphics(308, 100, 50), player.location)
animate(player,714)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
FreakUtils.teleport(player)
FreakUtils.giveFreakTask(player)
AntiMacro.terminateEventNpc(player)
openDialogue(player, FreakyForesterDialogue(), FreakUtils.freakNpc)
resetAnimator(player)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
} }

View file

@ -4,27 +4,40 @@ import core.game.node.entity.npc.NPC
import core.tools.RandomFunction import core.tools.RandomFunction
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import core.api.lock
import core.api.playAudio import core.api.playAudio
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
class GenieNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GENIE_409) { class GenieNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GENIE_409) {
val phrases = arrayOf("Greetings, @name!","Ehem... Master @name?","Are you there, Master @name?","No one ignores me!") lateinit var phrases: Array<String>
override fun tick() { override fun tick() {
if(RandomFunction.random(1,15) == 5){ sayLine(this, phrases, true, true)
sendChat(phrases.random().replace("@name",player.username.capitalize())) if (ticksLeft == 2) {
lock(player, 2)
} }
super.tick() super.tick()
} }
override fun init() { override fun init() {
super.init() super.init()
val honorific = if (player.isMale) "Master" else "Mistress"
phrases = arrayOf(
"Greetings, ${player.username}!",
"Ehem... $honorific ${player.username}?",
"Are you there, $honorific ${player.username}?",
"No one ignores me!"
)
playAudio(player, Sounds.GENIE_APPEAR_2301) playAudio(player, Sounds.GENIE_APPEAR_2301)
sendChat(phrases.random().replace("@name",player.username.capitalize()))
} }
override fun talkTo(npc: NPC) { override fun talkTo(npc: NPC) {
player.dialogueInterpreter.open(GenieDialogue(),npc) player.dialogueInterpreter.open(GenieDialogue(),npc)
} }
override fun onTimeUp() {
noteAndTeleport()
terminate()
}
} }

View file

@ -4,54 +4,29 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.TeleportManager
import core.game.system.timer.impl.AntiMacro
import core.game.world.map.Location
import core.game.world.map.build.DynamicRegion
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("Aha, you'll do ${player.username}!") sendChat("Aha, you'll do ${player.username}!")
face(player) face(player)
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> // Note: This event is NOT instanced:
when (stage) { // Sources:
0 -> { // https://youtu.be/2gpzn9oNdy0 (2007)
lock(player, 6) // https://youtu.be/Tni1HURgnxg (2008)
sendGraphics(Graphics(1576, 0, 0), player.location) // https://youtu.be/igdwDZOv9LU (2008)
animate(player,8939) // https://youtu.be/0oBCkLArUmc (2011 - even with personal Mysterious Old Man) - "Sorry, this is not the old man you are looking for."
playAudio(player, Sounds.TELEPORT_ALL_200) // https://youtu.be/FMuKZm-Ikgs (2011)
return@queueScript delayScript(player, 3) // val region = DynamicRegion.create(11591)
} kidnapPlayer(this, player, MazeInterface.STARTING_POINTS.random()) { player, _ ->
1 -> { MazeInterface.initMaze(player)
MazeInterface.initMaze(player) removeAttribute(player, MazeInterface.MAZE_ATTRIBUTE_CHESTS_OPEN)
// Note: This event is NOT instanced:
// Sources:
// https://youtu.be/2gpzn9oNdy0 (2007)
// https://youtu.be/Tni1HURgnxg (2008)
// https://youtu.be/igdwDZOv9LU (2008)
// https://youtu.be/0oBCkLArUmc (2011 - even with personal Mysterious Old Man) - "Sorry, this is not the old man you are looking for."
// https://youtu.be/FMuKZm-Ikgs (2011)
// val region = DynamicRegion.create(11591)
kidnapPlayer(player, MazeInterface.STARTING_POINTS.random(), TeleportManager.TeleportType.INSTANT) // 10 random spots
AntiMacro.terminateEventNpc(player)
sendGraphics(Graphics(1577, 0, 0), player.location)
animate(player,8941)
removeAttribute(player, MazeInterface.MAZE_ATTRIBUTE_CHESTS_OPEN)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
} }
override fun talkTo(npc: NPC) { override fun talkTo(npc: NPC) {
// Do nothing. sendMessage(player, "He isn't interested in talking to you.")
} }
} }

View file

@ -4,47 +4,22 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength import core.game.component.Component.setUnclosable
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.TeleportManager
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.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) { class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("${player.username}, you're under arrest!") sendChat("${player.username}, you're under arrest!")
face(player) face(player)
player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.") kidnapPlayer(this, player, PilloryInterface.LOCATIONS.random()) { player, _ ->
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> PilloryInterface.initPillory(player)
when (stage) { setUnclosable(player, player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from."))
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 -> {
PilloryInterface.initPillory(player)
val dest = PilloryInterface.LOCATIONS.random() //9 random spots!
kidnapPlayer(player, dest, TeleportManager.TeleportType.INSTANT)
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) { override fun talkTo(npc: NPC) {
//player.dialogueInterpreter.open(FreakyForesterDialogue(),npc) sendMessage(player, "He isn't interested in talking to you.")
} }
} }

View file

@ -5,15 +5,17 @@ import core.ServerConstants
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.api.utils.WeightedItem import core.api.utils.WeightedItem
import core.game.dialogue.DialogueFile import core.game.component.Component
import core.game.dialogue.DialogueLabeller
import core.game.dialogue.DialogueOption
import core.game.dialogue.FacialExpression import core.game.dialogue.FacialExpression
import core.game.interaction.QueueStrength import core.game.interaction.QueueStrength
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.tools.END_DIALOGUE
import org.rs09.consts.Components import org.rs09.consts.Components
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs
class QuizMasterDialogueFile : DialogueFile() { class QuizMasterDialogueFile : DialogueLabeller() {
companion object { companion object {
const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191 const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191
const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct" const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct"
@ -74,56 +76,77 @@ class QuizMasterDialogueFile : DialogueFile() {
} }
override fun handle(componentID: Int, buttonID: Int) { override fun addConversation() {
when (stage) { assignToIds(NPCs.QUIZ_MASTER_2477)
0 -> npc(FacialExpression.FRIENDLY,"WELCOME to the GREATEST QUIZ SHOW in the", "whole of ${ServerConstants.SERVER_NAME}:", "<col=8A0808>O D D</col> <col=8A088A>O N E</col> <col=08088A>O U T</col>").also { stage++ } afterClose { player ->
1 -> player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...").also { stage++ } loadLabel(player, "question")
2 -> npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "<col=FF0000>${player?.username}</col>!", "Just pick the O D D O N E O U T.", "Four questions right, and then you win!").also { stage++ } }
3 -> {
setAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player!!)) npc(FacialExpression.FRIENDLY,"WELCOME to the GREATEST QUIZ SHOW in the", "whole of ${ServerConstants.SERVER_NAME}:", "<col=8A0808>O D D</col> <col=8A088A>O N E</col> <col=08088A>O U T</col>", unclosable = true)
player!!.interfaceManager.openChatbox(QUIZMASTER_INTERFACE) player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...", unclosable = true)
stage++ npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "<col=FF0000>${player?.username}</col>!", "Just pick the O D D O N E O U T.", "Four questions right, and then you win!", unclosable = true)
} goto("question")
4-> {
if (buttonID == getAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 0)) { label("question")
// Correct Answer manual(unclosable = true) { player, _ ->
setAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + 1) setAttribute(player, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player))
if (getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) >= 4) { val comp = Component(QUIZMASTER_INTERFACE)
npc(FacialExpression.FRIENDLY,"<col=08088A>CONGRATULATIONS!</col>", "You are a <col=8A0808>WINNER</col>!", "Please choose your <col=08088A>PRIZE</col>!") player.interfaceManager.openChatbox(comp)
stage = 5 return@manual comp
} else { }
npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!") exec { player, _ ->
stage = 3 if (buttonID == getAttribute(player, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 0)) {
} // Correct Answer
setAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, getAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + 1)
if (getAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) >= 4) {
goto("winner")
} else { } else {
// Wrong Answer goto("right")
npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!")
stage = 3
} }
} } else {
// Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas. goto("wrong")
5 -> options("1000 Coins", "Random Item").also { stage++ }
6 -> {
resetAnimator(player!!)
returnPlayer(player!!)
when (buttonID) {
1 -> {
queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int ->
addItemOrDrop(player!!, Items.COINS_995, 1000)
return@queueScript stopExecuting(player!!)
}
}
2 -> {
queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int ->
addItemOrDrop(player!!, tableRoll.roll()[0].id)
return@queueScript stopExecuting(player!!)
}
}
}
removeAttributes(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER)
stage = END_DIALOGUE
end()
} }
} }
label("right")
npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!", unclosable = true)
goto("question")
label("wrong")
npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!", unclosable = true)
goto("question")
label("winner")
npc(FacialExpression.FRIENDLY,"<col=08088A>CONGRATULATIONS!</col>", "You are a <col=8A0808>WINNER</col>!", "Please choose your <col=08088A>PRIZE</col>!", unclosable = true)
options(
DialogueOption("money", "1000 Coins", skipPlayer = true),
DialogueOption("item", "Random Item", skipPlayer = true)
)
label("money")
exec { player, _ ->
queueScript(player, 0, QueueStrength.SOFT) { _ ->
addItemOrDrop(player, Items.COINS_995, 1000)
return@queueScript stopExecuting(player)
}
}
goto("cleanup")
label("item")
exec { player, _ ->
queueScript(player, 0, QueueStrength.SOFT) { _ ->
addItemOrDrop(player, tableRoll.roll()[0].id)
return@queueScript stopExecuting(player)
}
}
goto("cleanup")
label("cleanup")
exec { player, _ ->
resetAnimator(player)
returnPlayer(player)
removeAttributes(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER)
}
goto("nowhere")
} }
} }

View file

@ -4,14 +4,9 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.TeleportManager
import core.game.system.timer.impl.AntiMacro
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
/** /**
* Quiz Master NPC: * Quiz Master NPC:
@ -27,35 +22,14 @@ class QuizMasterNPC(var type: String = "", override var loot: WeightBasedTable?
override fun init() { override fun init() {
super.init() super.init()
sendChat("Hey ${player.username}! It's your lucky day!") sendChat("Hey ${player.username}! It's your lucky day!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(1952, 4764, 0)) { player, _ ->
0 -> { setAttribute(player, QuizMasterDialogueFile.QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0)
lock(player, 6) sendMessage(player, "Answer four questions correctly in a row to be teleported back where you came from.")
sendGraphics(Graphics(1576, 0, 0), player.location) sendMessage(player, "You will need to relog in if you lose the quiz dialog.") // Inauthentic, but there to notify the player in case.
animate(player,8939) face(player, Location(1952, 4768, 1))
playAudio(player, Sounds.TELEPORT_ALL_200) animate(player,2378)
return@queueScript delayScript(player, 3) // Quiz dialogue gets opened automatically on zone entry.
}
1 -> {
kidnapPlayer(player, Location(1952, 4764, 1), TeleportManager.TeleportType.INSTANT)
setAttribute(player, QuizMasterDialogueFile.QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0)
AntiMacro.terminateEventNpc(player)
sendGraphics(Graphics(1577, 0, 0), player.location)
animate(player,8941)
sendMessage(player, "Answer four questions correctly in a row to be teleported back where you came from.")
sendMessage(player, "You will need to relog in if you lose the quiz dialog.") // Inauthentic, but there to notify the player in case.
return@queueScript delayScript(player, 6)
}
2 -> {
face(player, Location(1952, 4768, 1))
animate(player,2378)
// This is not needed as when you enter the QuizMasterBorders, it should fire off the dialogue
// openDialogue(player, QuizMasterDialogueFile(), this.asNpc())
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
} }

View file

@ -0,0 +1,25 @@
package content.global.ame.events.rockgolem
import core.game.node.entity.Entity
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.combat.CombatSwingHandler
import core.game.node.entity.combat.MultiSwingHandler
import core.game.node.entity.combat.equipment.SwitchAttack
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
import org.rs09.consts.NPCs
class RockGolemBehavior() : NPCBehavior(
NPCs.ROCK_GOLEM_413, NPCs.ROCK_GOLEM_414, NPCs.ROCK_GOLEM_415, NPCs.ROCK_GOLEM_416, NPCs.ROCK_GOLEM_417, NPCs.ROCK_GOLEM_418
) {
val rangeHandler = SwitchAttack(CombatStyle.RANGE)
val meleeHandler = SwitchAttack(CombatStyle.MELEE)
val combatHandler = MultiSwingHandler(rangeHandler, meleeHandler)
override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler {
return combatHandler
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return super.getXpMultiplier(self, attacker) / 16.0
}
}

View file

@ -5,32 +5,49 @@ import core.tools.RandomFunction
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import core.api.lock
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
class SandwichLadyRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SANDWICH_LADY_3117) { class SandwichLadyRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SANDWICH_LADY_3117) {
val phrases = arrayOf("Hello, @name, can you hear me?","Sandwiches, @name!","Are you ignoring me, @name??","Yoohoo! Sandwiches, @name!","Hello, @name?", "Come get your sandwiches, @name!", "How could you ignore me like this, @name?!", "Do you even want your sandwiches, @name?") lateinit var phrases: Array<String>
var assigned_item = 0 var assigned_item = 0
val items = arrayOf(Items.BAGUETTE_6961,Items.TRIANGLE_SANDWICH_6962,Items.SQUARE_SANDWICH_6965,Items.ROLL_6963,Items.MEAT_PIE_2327,Items.KEBAB_1971,Items.CHOCOLATE_BAR_1973) val items = arrayOf(Items.BAGUETTE_6961, Items.TRIANGLE_SANDWICH_6962, Items.SQUARE_SANDWICH_6965, Items.ROLL_6963, Items.MEAT_PIE_2327, Items.KEBAB_1971, Items.CHOCOLATE_BAR_1973)
override fun tick() { override fun tick() {
if(RandomFunction.random(1,15) == 5){ sayLine(this, phrases, true, true)
sendChat(phrases.random().replace("@name",player.username.capitalize())) if (ticksLeft == 2) {
lock(player, 2)
} }
super.tick() super.tick()
} }
override fun init() { override fun init() {
super.init() super.init()
phrases = arrayOf(
// https://www.youtube.com/watch?v=ek8r3ZS929E
// She always starts with "Sandwiches, ${player.username}!" but she ALSO picks that at random, hence duplicate it with hasOpeningPhrase = true
"Sandwiches, ${player.username}!",
"Sandwiches, ${player.username}!",
"Come on ${player.username}, I made these specially!!",
"All types of sandwiches, ${player.username}.",
"Did you hear me ${player.username}?",
"You think I made these just for fun?!!?",
"How could you ignore me like this, ${player.username}?!" //unknown if authentic but it was already here
)
assignItem() assignItem()
sendChat(phrases.random().replace("@name",player.username.capitalize())) }
override fun onTimeUp() {
noteAndTeleport()
terminate()
} }
fun assignItem(){ fun assignItem(){
assigned_item = items.random() assigned_item = items.random()
player.setAttribute("sandwich-lady:item",assigned_item) player.setAttribute("sandwich-lady:item", assigned_item)
} }
override fun talkTo(npc: NPC) { override fun talkTo(npc: NPC) {
player.dialogueInterpreter.open(SandwichLadyDialogue(false),npc) player.dialogueInterpreter.open(SandwichLadyDialogue(false), npc)
} }
} }

View file

@ -5,32 +5,16 @@ import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.TeleportManager
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Sounds
class MysteriousOldManNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { class MysteriousOldManNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("Surprise exam, ${player.username}!") sendChat("Surprise exam, ${player.username}!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(1886, 5025, 0)) { _, _ ->
0 -> { /* nothing needed */
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 -> {
kidnapPlayer(player, Location(1886, 5025, 0), TeleportManager.TeleportType.INSTANT)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
} }

View file

@ -2,7 +2,6 @@ package content.global.ame.events.surpriseexam
import core.game.component.Component import core.game.component.Component
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.map.Location import core.game.world.map.Location
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
@ -66,6 +65,6 @@ class SupriseExamListeners : InteractionListener, MapArea {
} }
override fun getRestrictions(): Array<ZoneRestriction> { override fun getRestrictions(): Array<ZoneRestriction> {
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP) return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP)
} }
} }

View file

@ -1,15 +1,12 @@
package content.global.ame.events.surpriseexam package content.global.ame.events.surpriseexam
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer import content.global.ame.returnPlayer
import core.api.* import core.api.*
import core.game.node.entity.impl.PulseType import core.game.node.entity.impl.PulseType
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.system.task.Pulse import core.game.system.task.Pulse
import core.game.world.map.Location
import org.rs09.consts.Components import org.rs09.consts.Components
import org.rs09.consts.Items import org.rs09.consts.Items
import core.game.node.entity.player.link.TeleportManager
object SurpriseExamUtils { object SurpriseExamUtils {
val SE_KEY_INDEX = "supexam:index" val SE_KEY_INDEX = "supexam:index"

View file

@ -1,12 +1,12 @@
package content.global.bots package content.global.bots
import content.global.skill.fletching.log.LogCraftInfo
import content.global.skill.fletching.log.CraftItemWithLogScript
import core.game.bots.Script
import core.game.bots.SkillingBotAssembler
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import content.global.skill.fletching.Fletching
import content.global.skill.fletching.FletchingPulse
import core.game.node.item.Item import core.game.node.item.Item
import org.rs09.consts.Items import org.rs09.consts.Items
import core.game.bots.SkillingBotAssembler
import core.game.bots.Script
class FletchingBankstander : Script(){ class FletchingBankstander : Script(){
var state = State.FLETCHING var state = State.FLETCHING
@ -17,7 +17,7 @@ class FletchingBankstander : Script(){
State.FLETCHING -> { State.FLETCHING -> {
bot.inventory.add(Item(Items.KNIFE_946)) bot.inventory.add(Item(Items.KNIFE_946))
bot.inventory.add(Item(Items.LOGS_1511,27)) bot.inventory.add(Item(Items.LOGS_1511,27))
bot.pulseManager.run(FletchingPulse(bot, Item(Items.LOGS_1511),27, Fletching.FletchingItems.ARROW_SHAFT)) CraftItemWithLogScript(bot, LogCraftInfo.ARROW_SHAFT, 27).invoke()
State.BANKING State.BANKING
} }

View file

@ -1,181 +0,0 @@
package content.global.dialogue
import core.api.*
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.IronmanMode
import core.plugin.Initializable
import core.game.dialogue.IfTopic
import core.game.dialogue.Topic
import content.global.handlers.npc.BankerNPC
import core.tools.END_DIALOGUE
import core.tools.START_DIALOGUE
@Initializable
class BankerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) {
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
when (stage) {
START_DIALOGUE -> when {
hasIronmanRestriction(player, IronmanMode.ULTIMATE) -> {
npcl(
core.game.dialogue.FacialExpression.ANNOYED,
"My apologies, dear ${if (player.isMale) "sir" else "madam"}, " +
"our services are not available for Ultimate ${if (player.isMale) "Ironmen" else "Ironwomen"}"
).also { stage = END_DIALOGUE }
}
else -> {
npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"Good day, how may I help you?"
).also {
if (hasAwaitingGrandExchangeCollections(player)) {
stage++
} else {
stage += 2
}
}
}
}
1 -> npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"Before we go any further, I should inform you that you " +
"have items ready for collection from the Grand Exchange."
).also { stage++ }
2 -> showTopics(
Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to access my bank account, please.", 10),
IfTopic(
core.game.dialogue.FacialExpression.FRIENDLY,
"I'd like to switch to my ${getBankAccountName(player, true)} bank account.",
13,
hasActivatedSecondaryBankAccount(player)
),
IfTopic(
core.game.dialogue.FacialExpression.FRIENDLY,
"I'd like to open a secondary bank account.",
20,
!hasActivatedSecondaryBankAccount(player)
),
Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to check my PIN settings.", 11),
Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to collect items.", 12),
Topic(core.game.dialogue.FacialExpression.ASKING, "What is this place?", 3),
)
3 -> npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"This is a branch of the Bank of Gielinor. We have branches in many towns."
).also { stage++ }
4 -> playerl(
core.game.dialogue.FacialExpression.ASKING,
"And what do you do?"
).also { stage++ }
5 -> npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"We will look after your items and money for you. " +
"Leave your valuables with us if you want to keep them safe."
).also { stage = END_DIALOGUE }
10 -> {
openBankAccount(player)
end()
}
11 -> {
openBankPinSettings(player)
end()
}
12 -> {
openGrandExchangeCollectionBox(player)
end()
}
13 -> {
toggleBankAccount(player)
npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"Your active bank account has been switched. " +
"You can now access your ${getBankAccountName(player)} account."
).also { stage = 2 }
}
20 -> npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"Certainly. We offer secondary accounts to all our customers."
).also { stage++ }
21 -> npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"The secondary account comes with a standard fee of 5,000,000 coins. The fee is non-refundable " +
"and account activation is permanent."
).also { stage++ }
22 -> npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"If your inventory does not contain enough money to cover the costs, we will complement " +
"the amount with the money inside your primary bank account."
).also { stage++ }
23 -> npcl(
core.game.dialogue.FacialExpression.ASKING,
"Knowing all this, would you like to proceed with opening your secondary bank account?"
).also { stage++ }
24 -> showTopics(
Topic(core.game.dialogue.FacialExpression.HAPPY, "Yes, I am still interested.", 25),
Topic(core.game.dialogue.FacialExpression.ANNOYED, "Actually, I've changed my mind.", 26)
)
25 -> {
when (activateSecondaryBankAccount(player)) {
SecondaryBankAccountActivationResult.ALREADY_ACTIVE -> {
npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"Your bank account was already activated, there is no need to pay twice."
).also { stage = END_DIALOGUE }
}
SecondaryBankAccountActivationResult.INTERNAL_FAILURE -> {
npcl(
core.game.dialogue.FacialExpression.ANNOYED,
"I must apologize, the transaction was not successful. Please check your " +
"primary bank account and your inventory - if there's money missing, please " +
"screenshot your chat box and contact the game developers."
).also { stage = END_DIALOGUE }
}
SecondaryBankAccountActivationResult.NOT_ENOUGH_MONEY -> {
npcl(
core.game.dialogue.FacialExpression.ANNOYED,
"It appears that you do not have the money necessary to cover the costs " +
"associated with opening a secondary bank account. I will be waiting here " +
"until you do."
).also { stage = END_DIALOGUE }
}
SecondaryBankAccountActivationResult.SUCCESS -> {
npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"Your secondary bank account has been opened and can be accessed through any " +
"of the Bank of Gielinor's employees. Thank you for choosing our services."
).also { stage = END_DIALOGUE }
}
}
}
26 -> npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"Very well. Should you decide a secondary bank account is needed, do not hesitate to " +
"contact any of the Bank of Gielinor's stationary employees. We will be happy to help."
).also { stage = END_DIALOGUE }
}
return true
}
override fun getIds(): IntArray = BankerNPC.NPC_IDS
}

View file

@ -1,5 +1,6 @@
package content.global.handlers.iface package content.global.handlers.iface
import content.global.handlers.iface.BookInterface.Companion.FANCY_BOOK_2_27
import core.api.closeInterface import core.api.closeInterface
import core.api.getAttribute import core.api.getAttribute
import core.api.openInterface import core.api.openInterface
@ -158,9 +159,22 @@ class BookInterface : InterfaceListener {
if (pageSet == getAttribute(player, CURRENT_PAGE_ATTRIBUTE, 0)) { if (pageSet == getAttribute(player, CURRENT_PAGE_ATTRIBUTE, 0)) {
player.packetDispatch.sendInterfaceConfig(componentId, enableLineId, false) player.packetDispatch.sendInterfaceConfig(componentId, enableLineId, false)
player.packetDispatch.sendModelOnInterface(modelId, componentId, drawLineId, 0) player.packetDispatch.sendModelOnInterface(modelId, componentId, drawLineId, 0)
player.packetDispatch.sendAngleOnInterface(componentId, drawLineId, zoom, pitch, yaw) player.packetDispatch.sendAngleOnInterface(componentId, drawLineId, zoom, pitch, yaw)
} else { }
player.packetDispatch.sendInterfaceConfig(componentId, enableLineId, true) }
/** Sets item on lineId of pageSet (0 index). Call this in the display function after pageSetup. */
fun setItemOnPage(player: Player, pageSet: Int, itemId: Int, componentId: Int, enableLineId: Int, drawLineId: Int, zoom: Int, pitch: Int, yaw: Int) {
if (pageSet == getAttribute(player, CURRENT_PAGE_ATTRIBUTE, 0)) {
player.packetDispatch.sendInterfaceConfig(componentId, enableLineId, false)
player.packetDispatch.sendItemOnInterface(itemId, 1, componentId, drawLineId)
}
}
/** Clears models(pictures) on lineId of pageSet (0 index). Call this in the display function after pageSetup. */
fun clearModelsOnPage(player: Player, componentId: Int) {
BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS.forEach { drawId ->
player.packetDispatch.sendInterfaceConfig(componentId, drawId, true);
} }
} }

View file

@ -174,7 +174,7 @@ class HairDresserInterface : ComponentPlugin(){
when(button){ when(button){
199 -> player.setAttribute("beard-setting",false) 199 -> player.setAttribute("beard-setting",false)
200 -> player.setAttribute("beard-setting",true) 200 -> player.setAttribute("beard-setting",true)
196,274 -> pay(player) 196,274,68,100 -> pay(player)
else -> when(component?.id){ else -> when(component?.id){
592 -> { //Female 592 -> { //Female
if(femaleColorButtonRange.contains(button)){ if(femaleColorButtonRange.contains(button)){

View file

@ -48,6 +48,7 @@ class EmptyOptionListener : InteractionListener {
POTION(Items.POTION_195, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401), 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), 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), 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_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_MILKY(Items.NETTLE_TEA_4240, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
BURNT_CURRY(Items.BURNT_CURRY_2013, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401), BURNT_CURRY(Items.BURNT_CURRY_2013, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),

View file

@ -1,85 +1,94 @@
package content.global.handlers.item package content.global.handlers.item
import core.ServerConstants
import core.api.* import core.api.*
import core.game.interaction.IntType import core.game.interaction.IntType
import core.game.interaction.InteractionListener import core.game.interaction.InteractionListener
import core.game.interaction.QueueStrength import core.game.interaction.QueueStrength
import core.game.node.entity.player.info.LogType
import core.game.node.entity.player.info.PlayerMonitor
import core.game.node.item.Item
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Animations
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
class EnchantJewelleryTabListener : InteractionListener { class EnchantJewelleryTabListener : InteractionListener {
private val LVL_1_ENCHANT = mapOf( private val LVL_1_ENCHANT = mapOf(
Items.SAPPHIRE_RING_1637 to Items.RING_OF_RECOIL_2550, Items.SAPPHIRE_RING_1637 to Items.RING_OF_RECOIL_2550,
Items.SAPPHIRE_NECKLACE_1656 to Items.GAMES_NECKLACE8_3853, Items.SAPPHIRE_NECKLACE_1656 to Items.GAMES_NECKLACE8_3853,
Items.SAPPHIRE_AMULET_1694 to Items.AMULET_OF_MAGIC_1727, Items.SAPPHIRE_AMULET_1694 to Items.AMULET_OF_MAGIC_1727,
Items.SAPPHIRE_BRACELET_11072 to Items.BRACELET_OF_CLAY_11074 Items.SAPPHIRE_BRACELET_11072 to Items.BRACELET_OF_CLAY_11074
) )
private val LVL_2_ENCHANT = mapOf( private val LVL_2_ENCHANT = mapOf(
Items.EMERALD_RING_1639 to Items.RING_OF_DUELLING8_2552, Items.EMERALD_RING_1639 to Items.RING_OF_DUELLING8_2552,
Items.EMERALD_NECKLACE_1658 to Items.BINDING_NECKLACE_5521, Items.EMERALD_NECKLACE_1658 to Items.BINDING_NECKLACE_5521,
Items.EMERALD_AMULET_1696 to Items.AMULET_OF_DEFENCE_1729, Items.EMERALD_AMULET_1696 to Items.AMULET_OF_DEFENCE_1729,
Items.EMERALD_BRACELET_11076 to Items.CASTLEWAR_BRACE3_11079 Items.EMERALD_BRACELET_11076 to Items.CASTLEWAR_BRACE3_11079
) )
private val LVL_3_ENCHANT = mapOf( private val LVL_3_ENCHANT = mapOf(
Items.RUBY_RING_1641 to Items.RING_OF_FORGING_2568, Items.RUBY_RING_1641 to Items.RING_OF_FORGING_2568,
Items.RUBY_NECKLACE_1660 to Items.DIGSITE_PENDANT_5_11194, Items.RUBY_NECKLACE_1660 to Items.DIGSITE_PENDANT_5_11194,
Items.RUBY_AMULET_1698 to Items.AMULET_OF_STRENGTH_1725, Items.RUBY_AMULET_1698 to Items.AMULET_OF_STRENGTH_1725,
Items.RUBY_BRACELET_11085 to Items.INOCULATION_BRACE_11088 Items.RUBY_BRACELET_11085 to Items.INOCULATION_BRACE_11088
) )
private val LVL_4_ENCHANT = mapOf( private val LVL_4_ENCHANT = mapOf(
Items.DIAMOND_RING_1643 to Items.RING_OF_LIFE_2570, Items.DIAMOND_RING_1643 to Items.RING_OF_LIFE_2570,
Items.DIAMOND_NECKLACE_1662 to Items.PHOENIX_NECKLACE_11090, Items.DIAMOND_NECKLACE_1662 to Items.PHOENIX_NECKLACE_11090,
Items.DIAMOND_AMULET_1700 to Items.AMULET_OF_POWER_1731, Items.DIAMOND_AMULET_1700 to Items.AMULET_OF_POWER_1731,
Items.DIAMOND_BRACELET_11092 to Items.FORINTHRY_BRACE5_11095 Items.DIAMOND_BRACELET_11092 to Items.FORINTHRY_BRACE5_11095
) )
private val LVL_5_ENCHANT = mapOf( private val LVL_5_ENCHANT = mapOf(
Items.DRAGONSTONE_RING_1645 to Items.RING_OF_WEALTH4_14646, Items.DRAGONSTONE_RING_1645 to Items.RING_OF_WEALTH_2572,
Items.DRAGON_NECKLACE_1664 to Items.SKILLS_NECKLACE4_11105, Items.DRAGON_NECKLACE_1664 to Items.SKILLS_NECKLACE_11113,
Items.DRAGONSTONE_AMMY_1702 to Items.AMULET_OF_GLORY4_1712, Items.DRAGONSTONE_AMMY_1702 to Items.AMULET_OF_GLORY_1704,
Items.DRAGON_BRACELET_11115 to Items.COMBAT_BRACELET4_11118 Items.DRAGON_BRACELET_11115 to Items.COMBAT_BRACELET_11126
) )
private val LVL_6_ENCHANT = mapOf( private val LVL_6_ENCHANT = mapOf(
Items.ONYX_RING_6575 to Items.RING_OF_STONE_6583, Items.ONYX_RING_6575 to Items.RING_OF_STONE_6583,
Items.ONYX_NECKLACE_6577 to Items.BERSERKER_NECKLACE_11128, Items.ONYX_NECKLACE_6577 to Items.BERSERKER_NECKLACE_11128,
Items.ONYX_AMULET_6581 to Items.AMULET_OF_FURY_6585, Items.ONYX_AMULET_6581 to Items.AMULET_OF_FURY_6585,
Items.ONYX_BRACELET_11130 to Items.REGEN_BRACELET_11133 Items.ONYX_BRACELET_11130 to Items.REGEN_BRACELET_11133
)
private val TAB_MAPPING = arrayOf(
Pair(Items.ENCHANT_SAPPHIRE_8016, LVL_1_ENCHANT),
Pair(Items.ENCHANT_EMERALD_8017, LVL_2_ENCHANT),
Pair(Items.ENCHANT_RUBY_8018, LVL_3_ENCHANT),
Pair(Items.ENCHANT_DIAMOND_8019, LVL_4_ENCHANT),
Pair(Items.ENCHANT_DRAGONSTN_8020, LVL_5_ENCHANT),
Pair(Items.ENCHANT_ONYX_8021, LVL_6_ENCHANT)
) )
override fun defineListeners() { override fun defineListeners() {
on(IntType.ITEM, "break") {player, node -> for ((tablet, mapping) in TAB_MAPPING) {
closeAllInterfaces(player) on(tablet, IntType.ITEM, "break") { player, _ ->
delayEntity(player, 1) sendMessage(player, "Try using the tablet on the item instead.") //TODO authentic message
queueScript(player, strength = QueueStrength.SOFT) { return@on true
}
val items = when (node.id) { for ((unenchanted, enchanted) in mapping) {
8016 -> LVL_1_ENCHANT //Sapphire onUseWith(IntType.ITEM, tablet, unenchanted) { player, tabItem, node ->
8017 -> LVL_2_ENCHANT var product = enchanted
8018 -> LVL_3_ENCHANT if (product == Items.RING_OF_WEALTH_2572 && ServerConstants.RING_OF_WEALTH_TELEPORT) {
8019 -> LVL_4_ENCHANT product = Items.RING_OF_WEALTH_14638
8020 -> LVL_5_ENCHANT }
8021 -> LVL_6_ENCHANT if (removeItem(player, Item(tabItem.id))) {
else -> return@queueScript stopExecuting(player) closeAllInterfaces(player)
} playAudio(player, Sounds.POH_TABLET_BREAK_979)
val anim = Animation(Animations.POH_TABLET_BREAK_4069)
if (inInventory(player, node.id)) { animate(player, anim, true)
for (item in player.inventory.toArray()) { delayEntity(player, anim.duration)
if (item == null) continue queueScript(player, anim.duration, QueueStrength.SOFT) {
val product = items[item.id] ?: continue val item = node.asItem()
if (removeItem(player, node.id) && (removeItem(player, item.id)) && addItem(player, product)) { val ret = replaceSlot(player, item.slot, Item(product), item)
playAudio(player, Sounds.POH_TABLET_BREAK_979) if (ret != item) {
animate(player, 4069, true) PlayerMonitor.log(player, LogType.DUPE_ALERT, "Unknown slot-replacement problem when enchanting jewellery (adding $product replaced $ret rather than $item)")
break }
return@queueScript stopExecuting(player)
} }
} }
return@onUseWith true
} }
return@queueScript stopExecuting(player)
} }
return@on true }
} }
}
} }

View file

@ -0,0 +1,244 @@
package content.global.handlers.item.equipment
import core.game.node.item.Item
import org.rs09.consts.Items
/**
* Barrows equipment information and utilities.
* @author 'Vexia - original code
* @author Damighty - Kotlin conversion and refactor
*/
object BarrowsEquipment {
// Barrows equipment lasts for 15 hours of combat. Each piece has 4 degradation tiers (100, 75, 50, 25).
const val DEGRADATION_TICKS_PER_TIER = (15 * 6000) / 4 // 22500 ticks per tier
private const val MAX_DURABILITY = DEGRADATION_TICKS_PER_TIER * 4
/**
* A data class for each Barrows piece. Holds all information related to a specific piece of Barrows gear.
*
* @param brother The Barrows brother this item belongs to.
* @param equipmentType The slot type ("weapon", "body", "legs", "helm").
* @param itemName The formatted name of the item.
* @param baseRepairCost The full repair cost in GP.
* @param degradationStates A list of item IDs, from fully repaired (index 0) to broken (index 5).
*/
data class BarrowsItemDefinition(
val brother: String,
val equipmentType: String,
val itemName: String,
val baseRepairCost: Int,
val degradationStates: List<Int>
) {
val repairedId: Int = degradationStates.first()
val brokenId: Int = degradationStates.last()
/** Checks if a given item ID belongs to this specific equipment set. */
fun contains(itemId: Int): Boolean = itemId in degradationStates
/** Gets the degradation index for a given item ID (0=repaired, 1=100, ..., 5=broken). */
fun getDegradationIndex(itemId: Int): Int = degradationStates.indexOf(itemId)
}
/** All Barrows equipment data. */
private val barrowsItemDefinitions = listOf(
// Dharok
BarrowsItemDefinition("dharok", "helm", "Dharok's helm", 60_000,
listOf(Items.DHAROKS_HELM_4716, Items.DHAROKS_HELM_100_4880,
Items.DHAROKS_HELM_75_4881, Items.DHAROKS_HELM_50_4882,
Items.DHAROKS_HELM_25_4883, Items.DHAROKS_HELM_0_4884)),
BarrowsItemDefinition("dharok", "weapon", "Dharok's greataxe", 100_000,
listOf(Items.DHAROKS_GREATAXE_4718, Items.DHAROKS_AXE_100_4886,
Items.DHAROKS_AXE_75_4887, Items.DHAROKS_AXE_50_4888,
Items.DHAROKS_AXE_25_4889, Items.DHAROKS_AXE_0_4890)),
BarrowsItemDefinition("dharok", "body", "Dharok's platebody", 90_000,
listOf(Items.DHAROKS_PLATEBODY_4720, Items.DHAROKS_BODY_100_4892,
Items.DHAROKS_BODY_75_4893, Items.DHAROKS_BODY_50_4894,
Items.DHAROKS_BODY_25_4895, Items.DHAROKS_BODY_0_4896)),
BarrowsItemDefinition("dharok", "legs", "Dharok's platelegs", 80_000,
listOf(Items.DHAROKS_PLATELEGS_4722, Items.DHAROKS_LEGS_100_4898,
Items.DHAROKS_LEGS_75_4899, Items.DHAROKS_LEGS_50_4900,
Items.DHAROKS_LEGS_25_4901, Items.DHAROKS_LEGS_0_4902)),
// Guthan
BarrowsItemDefinition("guthan", "helm", "Guthan's helm", 60_000,
listOf(Items.GUTHANS_HELM_4724, Items.GUTHANS_HELM_100_4904,
Items.GUTHANS_HELM_75_4905, Items.GUTHANS_HELM_50_4906,
Items.GUTHANS_HELM_25_4907, Items.GUTHANS_HELM_0_4908)),
BarrowsItemDefinition("guthan", "weapon", "Guthan's warspear", 100_000,
listOf(Items.GUTHANS_WARSPEAR_4726, Items.GUTHANS_SPEAR_100_4910,
Items.GUTHANS_SPEAR_75_4911, Items.GUTHANS_SPEAR_50_4912,
Items.GUTHANS_SPEAR_25_4913, Items.GUTHANS_SPEAR_0_4914)),
BarrowsItemDefinition("guthan", "body", "Guthan's platebody", 90_000,
listOf(Items.GUTHANS_PLATEBODY_4728, Items.GUTHANS_BODY_100_4916,
Items.GUTHANS_BODY_75_4917, Items.GUTHANS_BODY_50_4918,
Items.GUTHANS_BODY_25_4919, Items.GUTHANS_BODY_0_4920)),
BarrowsItemDefinition("guthan", "legs", "Guthan's chainskirt", 80_000,
listOf(Items.GUTHANS_CHAINSKIRT_4730, Items.GUTHANS_SKIRT_100_4922,
Items.GUTHANS_SKIRT_75_4923, Items.GUTHANS_SKIRT_50_4924,
Items.GUTHANS_SKIRT_25_4925, Items.GUTHANS_SKIRT_0_4926)),
// Torag
BarrowsItemDefinition("torag", "helm", "Torag's helm", 60_000,
listOf(Items.TORAGS_HELM_4745, Items.TORAGS_HELM_100_4952,
Items.TORAGS_HELM_75_4953, Items.TORAGS_HELM_50_4954,
Items.TORAGS_HELM_25_4955, Items.TORAGS_HELM_0_4956)),
BarrowsItemDefinition("torag", "weapon", "Torag's hammers", 100_000,
listOf(Items.TORAGS_HAMMERS_4747, Items.TORAGS_HAMMER_100_4958,
Items.TORAGS_HAMMER_75_4959, Items.TORAGS_HAMMER_50_4960,
Items.TORAGS_HAMMER_25_4961, Items.TORAGS_HAMMER_0_4962)),
BarrowsItemDefinition("torag", "body", "Torag's platebody", 90_000,
listOf(Items.TORAGS_PLATEBODY_4749, Items.TORAGS_BODY_100_4964,
Items.TORAGS_BODY_75_4965, Items.TORAGS_BODY_50_4966,
Items.TORAGS_BODY_25_4967, Items.TORAGS_BODY_0_4968)),
BarrowsItemDefinition("torag", "legs", "Torag's platelegs", 80_000,
listOf(Items.TORAGS_PLATELEGS_4751, Items.TORAGS_LEGS_100_4970,
Items.TORAGS_LEGS_75_4971, Items.TORAGS_LEGS_50_4972,
Items.TORAGS_LEGS_25_4973, Items.TORAGS_LEGS_0_4974)),
// Verac
BarrowsItemDefinition("verac", "helm", "Verac's helm", 60_000,
listOf(Items.VERACS_HELM_4753, Items.VERACS_HELM_100_4976,
Items.VERACS_HELM_75_4977, Items.VERACS_HELM_50_4978,
Items.VERACS_HELM_25_4979, Items.VERACS_HELM_0_4980)),
BarrowsItemDefinition("verac", "weapon", "Verac's flail", 100_000,
listOf(Items.VERACS_FLAIL_4755, Items.VERACS_FLAIL_100_4982,
Items.VERACS_FLAIL_75_4983, Items.VERACS_FLAIL_50_4984,
Items.VERACS_FLAIL_25_4985, Items.VERACS_FLAIL_0_4986)),
BarrowsItemDefinition("verac", "body", "Verac's brassard", 90_000,
listOf(Items.VERACS_BRASSARD_4757, Items.VERACS_TOP_100_4988,
Items.VERACS_TOP_75_4989, Items.VERACS_TOP_50_4990,
Items.VERACS_TOP_25_4991, Items.VERACS_TOP_0_4992)),
BarrowsItemDefinition("verac", "legs", "Verac's plateskirt", 80_000,
listOf(Items.VERACS_PLATESKIRT_4759, Items.VERACS_SKIRT_100_4994,
Items.VERACS_SKIRT_75_4995, Items.VERACS_SKIRT_50_4996,
Items.VERACS_SKIRT_25_4997, Items.VERACS_SKIRT_0_4998)),
// Ahrim
BarrowsItemDefinition("ahrim", "helm", "Ahrim's hood", 60_000,
listOf(Items.AHRIMS_HOOD_4708, Items.AHRIMS_HOOD_100_4856,
Items.AHRIMS_HOOD_75_4857, Items.AHRIMS_HOOD_50_4858,
Items.AHRIMS_HOOD_25_4859, Items.AHRIMS_HOOD_0_4860)),
BarrowsItemDefinition("ahrim", "weapon", "Ahrim's staff", 100_000,
listOf(Items.AHRIMS_STAFF_4710, Items.AHRIMS_STAFF_100_4862,
Items.AHRIMS_STAFF_75_4863, Items.AHRIMS_STAFF_50_4864,
Items.AHRIMS_STAFF_25_4865, Items.AHRIMS_STAFF_0_4866)),
BarrowsItemDefinition("ahrim", "body", "Ahrim's robetop", 90_000,
listOf(Items.AHRIMS_ROBETOP_4712, Items.AHRIMS_TOP_100_4868,
Items.AHRIMS_TOP_75_4869, Items.AHRIMS_TOP_50_4870,
Items.AHRIMS_TOP_25_4871, Items.AHRIMS_TOP_0_4872)),
BarrowsItemDefinition("ahrim", "legs", "Ahrim's robeskirt", 80_000,
listOf(Items.AHRIMS_ROBESKIRT_4714, Items.AHRIMS_SKIRT_100_4874,
Items.AHRIMS_SKIRT_75_4875, Items.AHRIMS_SKIRT_50_4876,
Items.AHRIMS_SKIRT_25_4877, Items.AHRIMS_SKIRT_0_4878)),
// Karil
BarrowsItemDefinition("karil", "helm", "Karil's coif", 60_000,
listOf(Items.KARILS_COIF_4732, Items.KARILS_COIF_100_4928,
Items.KARILS_COIF_75_4929, Items.KARILS_COIF_50_4930,
Items.KARILS_COIF_25_4931, Items.KARILS_COIF_0_4932)),
BarrowsItemDefinition("karil", "weapon", "Karil's crossbow", 100_000,
listOf(Items.KARILS_CROSSBOW_4734, Items.KARILS_X_BOW_100_4934,
Items.KARILS_X_BOW_75_4935, Items.KARILS_X_BOW_50_4936,
Items.KARILS_X_BOW_25_4937, Items.KARILS_X_BOW_0_4938)),
BarrowsItemDefinition("karil", "body", "Karil's leathertop", 90_000,
listOf(Items.KARILS_LEATHERTOP_4736, Items.KARILS_TOP_100_4940,
Items.KARILS_TOP_75_4941, Items.KARILS_TOP_50_4942,
Items.KARILS_TOP_25_4943, Items.KARILS_TOP_0_4944)),
BarrowsItemDefinition("karil", "legs", "Karil's leatherskirt", 80_000,
listOf(Items.KARILS_LEATHERSKIRT_4738, Items.KARILS_SKIRT_100_4946,
Items.KARILS_SKIRT_75_4947, Items.KARILS_SKIRT_50_4948,
Items.KARILS_SKIRT_25_4949, Items.KARILS_SKIRT_0_4950))
)
/** Cached access to an item's full definition from its ID */
private val itemIdToDefinitionMap: Map<Int, BarrowsItemDefinition> by lazy {
barrowsItemDefinitions.flatMap { def ->
def.degradationStates.map { id -> id to def }
}.toMap()
}
/** Gets all degradation state arrays for degradation registration */
fun getAllEquipmentSets(): Collection<IntArray> = barrowsItemDefinitions.map { it.degradationStates.toIntArray() }
/** Gets all repairable Barrows item IDs (anything not fully repaired) */
fun getAllRepairableBarrowsIds(): List<Int> = itemIdToDefinitionMap.filter { !isFullyRepaired(it.key) }.keys.toList()
/** Gets the full definition for a Barrows item */
@JvmStatic
fun getDefinition(itemId: Int): BarrowsItemDefinition? = itemIdToDefinitionMap[itemId]
/** Checks if an item ID is any Barrows item */
@JvmStatic
fun isBarrowsItem(itemId: Int): Boolean = itemId in itemIdToDefinitionMap
/** Checks if a Barrows item is fully repaired */
@JvmStatic
fun isFullyRepaired(itemId: Int): Boolean = getDefinition(itemId)?.repairedId == itemId
/** Checks if a Barrows item is broken */
@JvmStatic
fun isBroken(itemId: Int): Boolean = getDefinition(itemId)?.brokenId == itemId
/**
* Calculates the repair cost for a degraded Barrows item
* @param item The degraded Barrows item
* @return The repair cost in GP, or -1 if the item cannot be repaired
*/
fun getRepairCost(item: Item): Int {
val def = getDefinition(item.id) ?: return -1
if (isFullyRepaired(item.id)) return -1
val totalRemainingDurability = calculateTotalRemainingDurability(item, def)
val durabilityLost = MAX_DURABILITY - totalRemainingDurability
val cost = ((durabilityLost.toDouble() / MAX_DURABILITY) * def.baseRepairCost).toInt()
return cost
}
/**
* Reduces the durability of a Barrows item by 20% of its total remaining durability
* @param item The Barrows item
* @return The item with reduced durability, or null if the item is not a valid Barrows piece
*/
@JvmStatic
fun graveDeathDurabilityReduction(item: Item): Item? {
val def = getDefinition(item.id) ?: return null
if (isBroken(item.id)) return item
val totalRemainingDurability = calculateTotalRemainingDurability(item, def)
val durabilityReduction = (totalRemainingDurability * 0.2).toInt()
val newRemainingDurability = totalRemainingDurability - durabilityReduction
return createItemFromDurability(def, newRemainingDurability, item.amount)
}
/**
* Calculates the total remaining durability ticks (charge) for a Barrows item
*/
private fun calculateTotalRemainingDurability(item: Item, def: BarrowsItemDefinition): Int {
return when (val index = def.getDegradationIndex(item.id)) {
-1 -> 0 // Invalid
0 -> MAX_DURABILITY // Fully repaired
5 -> 0 // Broken
else -> {
// For degraded items (100, 75, 50, 25), durability is the number of fully remaining tiers below it, plus the charge of the current tier.
// Index 1 (100) has 3 tiers below it. Index 4 (25) has 0 tiers below it.
val remainingTiers = 4 - index
(remainingTiers * DEGRADATION_TICKS_PER_TIER) + item.charge
}
}
}
/**
* Creates a new Item instance based on a total durability value
*/
private fun createItemFromDurability(def: BarrowsItemDefinition, durability: Int, amount: Int): Item {
if (durability <= 0) {
return Item(def.brokenId, amount)
}
// Tier is 1-indexed (1=25, 2=50, 3=75, 4=100)
val tier = ((durability - 1) / DEGRADATION_TICKS_PER_TIER) + 1
// Degradation index is 4-indexed (4=25, 3=50, 2=75, 1=100)
val degradationIndex = 5 - tier
val newId = def.degradationStates[degradationIndex]
val newCharge = (durability - 1) % DEGRADATION_TICKS_PER_TIER + 1
return Item(newId, amount, newCharge)
}
}

View file

@ -4,73 +4,16 @@ import core.plugin.Initializable
import core.plugin.Plugin import core.plugin.Plugin
@Initializable @Initializable
class BarrowsEquipmentRegister : Plugin<Any>{ class BarrowsEquipmentRegister : Plugin<Any> {
public companion object {
// Barrows equipment lasts for 15 hours of combat, and each piece has 4 stages of degredation
@JvmField
public val TICKS = (15 * 6000) / 4
}
val DHAROK_HELM = arrayOf(4716,4880,4881,4882,4883,4884)
val DHAROK_AXE = arrayOf(4718,4886,4887,4888,4889,4890)
val DHAROK_BODY = arrayOf(4720,4892,4893,4894,4895,4896)
val DHAROK_LEGS = arrayOf(4722,4898,4899,4900,4901,4902)
val GUTHAN_HELM = arrayOf(4724,4904,4905,4906,4907,4908)
val GUTHAN_SPEAR = arrayOf(4726,4910,4911,4912,4913,4914)
val GUTHAN_BODY = arrayOf(4728,4916,4917,4918,4919,4920)
val GUTHAN_SKIRT = arrayOf(4730,4922,4923,4924,4925,4926)
val TORAG_HELM = arrayOf(4745,4952,4953,4954,4955,4956)
val TORAG_HAMMER = arrayOf(4747,4958,4959,4960,4961,4962)
val TORAG_BODY = arrayOf(4749,4964,4965,4966,4967,4968)
val TORAG_LEGS = arrayOf(4751,4970,4971,4972,4973,4974)
val VERAC_HELM = arrayOf(4753,4976,4977,4978,4979,4980)
val VERAC_FLAIL = arrayOf(4755,4982,4983,4984,4985,4986)
val VERAC_BRASS = arrayOf(4757,4988,4989,4990,4991,4992)
val VERAC_SKIRT = arrayOf(4759,4994,4995,4996,4997,4998)
val AHRIM_HOOD = arrayOf(4708,4856,4857,4858,4859,4860)
val AHRIM_STAFF = arrayOf(4710,4862,4863,4864,4865,4866)
val AHRIM_TOP = arrayOf(4712,4868,4869,4870,4871,4872)
val AHRIM_SKIRT = arrayOf(4714,4874,4875,4876,4877,4878)
val KARIL_COIF = arrayOf(4732,4928,4929,4930,4931,4932)
val KARIL_CBOW = arrayOf(4734,4934,4935,4936,4937,4938)
val KARIL_TOP = arrayOf(4736,4940,4941,4942,4943,4944)
val KARIL_SKIRT = arrayOf(4738,4946,4947,4948,4949,4950)
override fun newInstance(arg: Any?): Plugin<Any> { override fun newInstance(arg: Any?): Plugin<Any> {
EquipmentDegrader.registerSet(TICKS, AHRIM_HOOD) BarrowsEquipment.getAllEquipmentSets().forEach {
EquipmentDegrader.registerSet(TICKS, AHRIM_STAFF) degradationSet ->
EquipmentDegrader.registerSet(TICKS, AHRIM_TOP) EquipmentDegrader.registerSet(BarrowsEquipment.DEGRADATION_TICKS_PER_TIER, degradationSet.toTypedArray())
EquipmentDegrader.registerSet(TICKS, AHRIM_SKIRT) }
EquipmentDegrader.registerSet(TICKS, KARIL_COIF)
EquipmentDegrader.registerSet(TICKS, KARIL_CBOW)
EquipmentDegrader.registerSet(TICKS, KARIL_TOP)
EquipmentDegrader.registerSet(TICKS, KARIL_SKIRT)
EquipmentDegrader.registerSet(TICKS, DHAROK_HELM)
EquipmentDegrader.registerSet(TICKS, DHAROK_AXE)
EquipmentDegrader.registerSet(TICKS, DHAROK_BODY)
EquipmentDegrader.registerSet(TICKS, DHAROK_LEGS)
EquipmentDegrader.registerSet(TICKS, GUTHAN_HELM)
EquipmentDegrader.registerSet(TICKS, GUTHAN_SPEAR)
EquipmentDegrader.registerSet(TICKS, GUTHAN_BODY)
EquipmentDegrader.registerSet(TICKS, GUTHAN_SKIRT)
EquipmentDegrader.registerSet(TICKS, TORAG_HELM)
EquipmentDegrader.registerSet(TICKS, TORAG_HAMMER)
EquipmentDegrader.registerSet(TICKS, TORAG_BODY)
EquipmentDegrader.registerSet(TICKS, TORAG_LEGS)
EquipmentDegrader.registerSet(TICKS, VERAC_HELM)
EquipmentDegrader.registerSet(TICKS, VERAC_FLAIL)
EquipmentDegrader.registerSet(TICKS, VERAC_BRASS)
EquipmentDegrader.registerSet(TICKS, VERAC_SKIRT)
return this return this
} }
override fun fireEvent(identifier: String?, vararg args: Any?): Any { override fun fireEvent(identifier: String?, vararg args: Any?): Any {
return Unit return Unit
} }
} }

View file

@ -93,7 +93,7 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler {
} }
} }
entity.getSkills().addExperience(Skills.HITPOINTS, hit * 1.33, true); entity.getSkills().addExperience(Skills.HITPOINTS, hit * 1.33, true);
if (entity.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_DEFENSIVE) { if (entity.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_LONG_RANGE) {
entity.getSkills().addExperience(Skills.RANGE, hit * 2, true); entity.getSkills().addExperience(Skills.RANGE, hit * 2, true);
entity.getSkills().addExperience(Skills.DEFENCE, hit * 2, true); entity.getSkills().addExperience(Skills.DEFENCE, hit * 2, true);
} else { } else {

View file

@ -0,0 +1,24 @@
package content.global.handlers.item.withitem
import core.api.*
import org.rs09.consts.Items
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import core.game.node.entity.player.info.LogType
import core.game.node.entity.player.info.PlayerMonitor
import core.game.node.item.Item
class GrindToothListener : InteractionListener {
override fun defineListeners() {
onUseWith(IntType.ITEM, Items.PESTLE_AND_MORTAR_233, Items.SUQAH_TOOTH_9079) { player, _, with ->
val item = with as Item
val res = replaceSlot(player, item.slot, Item(Items.GROUND_TOOTH_9082))
if (res?.id == Items.SUQAH_TOOTH_9079) {
sendMessage(player, "You grind the suqah tooth to dust.") //https://www.youtube.com/watch?v=RdIcNH50v7I
} else {
PlayerMonitor.log(player, LogType.DUPE_ALERT, "Player ground item ${res?.name} instead of a suqah tooth - potential slot-based manipulation attempt")
}
return@onUseWith true
}
}
}

View file

@ -1,62 +1,54 @@
package content.global.handlers.npc package content.global.handlers.npc
import content.global.handlers.scenery.BankBoothListener
import core.ServerConstants
import core.api.* import core.api.*
import core.game.dialogue.ChatAnim
import core.game.dialogue.DialogueLabeller
import core.game.dialogue.DialogueOption
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.Node import core.game.node.Node
import core.game.node.entity.Entity import core.game.node.entity.Entity
import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.npc.AbstractNPC
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.IronmanMode
import core.game.world.map.Direction import core.game.world.map.Direction
import core.game.world.map.Location import core.game.world.map.Location
import core.plugin.Initializable import core.plugin.Initializable
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import content.global.handlers.scenery.BankBoothListener
/** /**
* Provides dialogue tree for all generic banker NPCs as well as * Provides dialogue tree for all generic banker NPCs as well as
* handles all the common interactions like 'bank' and 'collect'. * handles all the common interactions like 'bank' and 'collect'.
* *
* @author vddCore * @author vddCore
* @author Player Name
*/ */
@Initializable @Initializable
class BankerNPC : AbstractNPC, InteractionListener { class BankerNPC : AbstractNPC, InteractionListener {
val NPC_IDS = intArrayOf(
NPCs.BANKER_44, NPCs.BANKER_45, NPCs.BANKER_494, NPCs.BANKER_495, NPCs.BANKER_496, NPCs.BANKER_497,
NPCs.BANKER_498, NPCs.BANKER_499, NPCs.BANKER_1036, NPCs.BANKER_1360, NPCs.BANKER_2163, NPCs.BANKER_2164,
NPCs.BANKER_2354, NPCs.BANKER_2355, NPCs.BANKER_2568, NPCs.BANKER_2569, NPCs.BANKER_2570, NPCs.BANKER_3198,
NPCs.BANKER_3199, NPCs.BANKER_5258, NPCs.BANKER_5259, NPCs.BANKER_5260, NPCs.BANKER_5261, NPCs.BANKER_5776,
NPCs.BANKER_5777, NPCs.BANKER_5912, NPCs.BANKER_5913, NPCs.BANKER_6200, NPCs.BANKER_6532, NPCs.BANKER_6533,
NPCs.BANKER_6534, NPCs.BANKER_6535, NPCs.BANKER_7445, NPCs.BANKER_7446, NPCs.BANKER_7605,
NPCs.GUNDAI_902,
NPCs.GHOST_BANKER_1702, NPCs.GNOME_BANKER_166, NPCs.NARDAH_BANKER_3046, NPCs.MAGNUS_GRAM_5488, NPCs.TZHAAR_KET_ZUH_2619,
NPCs.SIRSAL_BANKER_4519, NPCs.FADLI_958, NPCs.BANK_TUTOR_4907, NPCs.JADE_4296,
NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050,
NPCs.BANKER_6538
)
companion object { companion object {
private const val LUNAR_ISLE_BANK_REGION = 8253 private const val LUNAR_ISLE_BANK_REGION = 8253
val SPECIAL_NPC_IDS = intArrayOf(
NPCs.SIRSAL_BANKER_4519, NPCs.FADLI_958, NPCs.BANK_TUTOR_4907, NPCs.JADE_4296,
NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050, NPCs.ARNOLD_LYDSPOR_3824,
/* Maximillian Sackville - Near Wilderness bounty-hunter area. */
NPCs.BANKER_6538
)
val NPC_IDS = intArrayOf(
NPCs.BANKER_44, NPCs.BANKER_45, NPCs.BANKER_494, NPCs.BANKER_495, NPCs.BANKER_496, NPCs.BANKER_497,
NPCs.BANKER_498, NPCs.BANKER_499, NPCs.BANKER_1036, NPCs.BANKER_1360, NPCs.BANKER_2163, NPCs.BANKER_2164,
NPCs.BANKER_2354, NPCs.BANKER_2355, NPCs.BANKER_2568, NPCs.BANKER_2569, NPCs.BANKER_2570, NPCs.BANKER_3198,
NPCs.BANKER_3199, NPCs.BANKER_5258, NPCs.BANKER_5259, NPCs.BANKER_5260, NPCs.BANKER_5261, NPCs.BANKER_5776,
NPCs.BANKER_5777, NPCs.BANKER_5912, NPCs.BANKER_5913, NPCs.BANKER_6200, NPCs.BANKER_6532, NPCs.BANKER_6533,
NPCs.BANKER_6534, NPCs.BANKER_6535, NPCs.BANKER_7445, NPCs.BANKER_7446, NPCs.BANKER_7605,
NPCs.GUNDAI_902,
NPCs.GHOST_BANKER_1702, NPCs.GNOME_BANKER_166, NPCs.NARDAH_BANKER_3046, NPCs.MAGNUS_GRAM_5488, NPCs.TZHAAR_KET_ZUH_2619,
)
private val ALL_BANKER_NPC_IDS = intArrayOf(
*SPECIAL_NPC_IDS,
*NPC_IDS
)
/** /**
* This is poorly named, but performs a few checks to see if the player * This is poorly named, but performs a few checks to see if the player
* is trying to access the bank on the Lunar Isle and returns a boolean * is trying to access the bank on the Lunar Isle and returns a boolean
* controlling whether or not to pass the quick bank or collection use. * controlling whether or not to pass the quick bank or collection use.
*
* TODO
* The location of this method is shit too. Find a better place for it?
*/ */
fun checkLunarIsleRestriction(player: Player, node: Node): Boolean { fun checkLunarIsleRestriction(player: Player, node: Node): Boolean {
if (node.location.regionId != LUNAR_ISLE_BANK_REGION) if (node.location.regionId != LUNAR_ISLE_BANK_REGION)
@ -121,37 +113,35 @@ class BankerNPC : AbstractNPC, InteractionListener {
private fun provideDestinationOverride(entity: Entity, node: Node): Location { private fun provideDestinationOverride(entity: Entity, node: Node): Location {
val npc = node as NPC val npc = node as NPC
return when(npc.id) { return when(npc.id) {
/* Ogress bankers are 2x2 with their spawn being offset to south-western tile. */ /* Ogress bankers are 2x2 with their spawn being offset to south-western tile. */
NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050 -> npc.location.transform(3, 1, 0)
NPCs.OGRESS_BANKER_7050 -> npc.location.transform(3, 1, 0)
NPCs.BANKER_6532, NPCs.BANKER_6533,
NPCs.BANKER_6534, NPCs.BANKER_6535 -> npc.location.transform(npc.direction, 1)
/* Magnus has no bank booth nearby so we need to handle that edge case here. */ /* Magnus has no bank booth nearby so we need to handle that edge case here. */
NPCs.MAGNUS_GRAM_5488 -> npc.location.transform(Direction.NORTH, 2) NPCs.MAGNUS_GRAM_5488 -> npc.location.transform(Direction.NORTH, 2)
/* Special-cased NPCs, idk why */
NPCs.BANKER_6532, NPCs.BANKER_6533, NPCs.BANKER_6534, NPCs.BANKER_6535 -> npc.location.transform(npc.direction, 1)
else -> { else -> {
if (npc is BankerNPC) { if (npc is BankerNPC) {
npc.findAdjacentBankBoothLocation()?.let { npc.findAdjacentBankBoothLocation()?.let {
return it.second return it.second
} }
} }
return npc.location return npc.location
} }
} }
} }
override fun defineListeners() { override fun defineListeners() {
on(ALL_BANKER_NPC_IDS, IntType.NPC, "bank", handler = Companion::attemptBank) on(NPC_IDS, IntType.NPC, "bank", handler = Companion::attemptBank)
on(ALL_BANKER_NPC_IDS, IntType.NPC, "collect", handler = Companion::attemptCollect) on(NPC_IDS, IntType.NPC, "collect", handler = Companion::attemptCollect)
on(NPC_IDS, IntType.NPC, "talk-to") { player, node ->
DialogueLabeller.open(player, BankerDialogueLabellerFile(), node as NPC)
return@on true
}
} }
override fun defineDestinationOverrides() { override fun defineDestinationOverrides() {
setDest(IntType.NPC, ALL_BANKER_NPC_IDS, "bank", "collect", "talk-to", handler = ::provideDestinationOverride) setDest(IntType.NPC, NPC_IDS, "bank", "collect", "talk-to", handler = ::provideDestinationOverride)
} }
override fun init() { override fun init() {
@ -167,5 +157,167 @@ class BankerNPC : AbstractNPC, InteractionListener {
} }
} }
override fun getIds(): IntArray = ALL_BANKER_NPC_IDS class BankerDialogueLabellerFile : DialogueLabeller() {
val BANKERS_WITH_EXTRA_OPTION = intArrayOf(NPCs.BANK_TUTOR_4907, NPCs.JADE_4296, NPCs.BANKER_6538)
override fun addConversation() {
exec { player, npc ->
if (npc.id == NPCs.SIRSAL_BANKER_4519 && !hasSealOfPassage(player)) {
loadLabel(player, "no seal of passage")
}
if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) {
if (npc.id == NPCs.JADE_4296) loadLabel(player, "uim for jade")
loadLabel(player, "uim")
}
if (npc.id == NPCs.JADE_4296) loadLabel(player, "hello for jade")
loadLabel(player, "hello")
}
label("no seal of passage")
npc(ChatAnim.ANNOYED, "What are you doing here, Fremennik?!")
player(ChatAnim.WORRIED, "I have a Seal of Pass...")
npc(ChatAnim.ANGRY, "No you don't! Begone!")
// todo: kick them out, but only one of the two banker types should do so (see historical wiki)
goto("nowhere")
label("uim for jade")
npc(ChatAnim.ANNOYED, "Greetings, warrior. I wish I could help you, but our services are not available for Ultimate Iron@g[men,women].")
goto("nowhere")
label("uim")
npc(ChatAnim.ANNOYED, "My apologies, dear @g[sir,madam], our services are not available for Ultimate Iron@g[men,women]")
goto("nowhere")
label("hello for jade")
npc("Greetings warrior, how may I help you?")
exec { player, _ -> loadLabel(player, if (hasAwaitingGrandExchangeCollections(player)) "ge collect" else "main options") }
label("hello")
npc("Good day, would you like to access your bank account?")
exec { player, _ -> loadLabel(player, if (hasAwaitingGrandExchangeCollections(player)) "ge collect" else "main options") }
label("ge collect")
npc("Before we go any further, I should inform you that you have items ready for collection from the Grand Exchange.")
goto("main options")
label("main options")
options(
DialogueOption("how to use", "How do I use the bank?") { _, npc -> return@DialogueOption npc.id == NPCs.BANK_TUTOR_4907 },
DialogueOption("who is the bounty hunter banker", "Who are you?") { _, npc -> return@DialogueOption npc.id == NPCs.BANKER_6538 },
DialogueOption("access", "I'd like to access my bank account please.", expression = ChatAnim.ASKING),
DialogueOption("buy second bank", "I'd like to open a secondary bank account.") { player, _ -> return@DialogueOption ServerConstants.SECOND_BANK && !hasActivatedSecondaryBankAccount(player) },
DialogueOption("switch second bank", "I'd like to switch to my primary bank account.") { player, _ -> return@DialogueOption hasActivatedSecondaryBankAccount(player) && isUsingSecondaryBankAccount(player) },
DialogueOption("switch second bank", "I'd like to switch to my secondary bank account.") { player, _ -> return@DialogueOption hasActivatedSecondaryBankAccount(player) && !isUsingSecondaryBankAccount(player) },
DialogueOption("pin", "I'd like to check my PIN settings."),
DialogueOption("collect", "I'd like to collect items."),
DialogueOption("what is this place", "What is this place?") { _, npc -> return@DialogueOption npc.id !in BANKERS_WITH_EXTRA_OPTION },
DialogueOption("jade's employment history", "How long have you worked here?") { _, npc -> return@DialogueOption npc.id == NPCs.JADE_4296 },
DialogueOption("is the bounty hunter banker afraid", "Aren't you afraid of working in the Wilderness?") { _, npc -> return@DialogueOption npc.id == NPCs.BANKER_6538 && !ServerConstants.SECOND_BANK }
)
label("how to use")
options(
DialogueOption("using bank", "Using the bank itself.", "Using the bank itself. I'm not sure how....?"),
DialogueOption("using deposit", "Using bank deposit boxes.", "Using Bank deposit boxes.... what are they?"),
DialogueOption("using PIN", "What's this PIN thing that people keep talking about?", "What's this PIN thing that people keep talking about?"),
DialogueOption("nowhere", "Goodbye.")
)
label("using bank")
npc("Speak to any banker and ask to see your bank", "account. If you have set a PIN you will be asked for", "it, then all the belongings you have placed in the bank", "will appear in the window. To withdraw one item, left-")
npc("click on it once.")
npc("To withdraw many, right-click on the item and select", "from the menu. The same for depositing, left-click on", "the item in your inventory to deposit it in the bank.", "Right-click on it to deposit many of the same items.")
npc("To move things around in your bank: firstly select", "Swap or Insert as your default moving mode, you can", "find these buttons on the bank window itself. Then click", "and drag an item to where you want it to appear.")
npc("You may withdraw 'notes' or 'certificates' when the", "items you are trying to withdraw do not stack in your", "inventory. This will only work for items which are", "tradeable.")
npc("For instance, if you wanted to sell 100 logs to another", "player, they would not fit in one inventory and you", "would need to do multiple trades. Instead, click the", "Note button to withdraw the logs as 'certs' or 'notes'")
npc("then withdraw the items you need.")
goto("how to use")
label("using deposit")
npc("They look like grey pillars, there's one just over there,", "near the desk. Bank deposit boxes save so much time.", "If you're simply wanting to deposit a single item, 'Use'", "it on the deposit box.")
npc("Otherwise, simply click once on the box and it will give", "you a choice of what to deposit in an interface very", "similar to the bank itself. Very quick for when you're", "simply fishing or mining etc.")
goto("how to use")
label("using PIN")
npc("The PIN - Personal Identification Number - can be", "set on your bank account to protect the items there in", "case someone finds out your account password. It", "consists of four numbers that you remember and tell")
npc("no one.")
npc("So if someone did manage to get your password they", "couldn't steal your items if they were in the bank.")
goto("how to use")
label("who is the bounty hunter banker")
npc(ChatAnim.NEUTRAL, "How inconsiderate of me, dear @g[sir,madam]. My name is Maximillian Sackville and I conduct operations here on behalf of The Bank of Gielinor.")
goto("main options")
label("access")
exec { player, _ -> openBankAccount(player) }
goto("nowhere")
label("buy second bank")
npc("Certainly. We offer secondary accounts to all our customers.")
npc("The secondary account comes with a standard fee of 5,000,000 coins. The fee is non-refundable and account activation is permanent.")
npc("If your inventory does not contain enough money to cover the costs, we will complement the amount with the money inside your primary bank account.")
npc("Knowing all this, would you like to proceed with opening your secondary bank account?")
options(
DialogueOption("buy second bank yes", "Yes, I am still interested.", expression = ChatAnim.HAPPY),
DialogueOption("buy second bank no", "Actually, I've changed my mind.", expression = ChatAnim.ANNOYED)
)
label("buy second bank yes")
exec { player, _ -> loadLabel(player, when (activateSecondaryBankAccount(player)) {
SecondaryBankAccountActivationResult.ALREADY_ACTIVE -> "already"
SecondaryBankAccountActivationResult.INTERNAL_FAILURE -> "failure"
SecondaryBankAccountActivationResult.NOT_ENOUGH_MONEY -> "not enough cash"
SecondaryBankAccountActivationResult.SUCCESS -> "success"
}) }
label("already")
npc(ChatAnim.FRIENDLY, "Your bank account was already activated, there is no need to pay twice.")
goto("main options")
label("failure")
npc(ChatAnim.ANNOYED, "I must apologize, the transaction was not successful. Please check your primary bank account and your inventory - if there's money missing, please screenshot your chatbox and contact the game developers.")
goto("main options")
label("not enough cash")
npc(ChatAnim.ANNOYED, "It appears that you do not have the money necessary to cover the costs associated with opening a secondary bank account. I will be waiting here until you do.")
goto("main options")
label("success")
npc(ChatAnim.FRIENDLY, "Your secondary bank account has been opened and can be accessed through any of the Bank of Gielinor's employees. Thank you for choosing our services.")
goto("main options")
label("buy second bank no")
npc("Very well. Should you decide a secondary bank account is needed, do not hesitate to contact any of the Bank of Gielinor's stationary employees. We will be happy to help.")
goto("main options")
label("switch second bank")
exec { player, _ ->
toggleBankAccount(player)
loadLabel(player, if (isUsingSecondaryBankAccount(player)) "now secondary" else "now primary")
}
label("now primary")
npc("Your active bank account has been switched. You can now access your primary account.")
goto("main options")
label("now secondary")
npc("Your active bank account has been switched. You can now access your secondary account.")
goto("main options")
label("pin")
exec { player, _ -> openBankPinSettings(player) }
goto("nowhere")
label("collect")
exec { player, _ -> openGrandExchangeCollectionBox(player) }
goto("nowhere")
label("what is this place")
npc("This is a branch of the Bank of Gielinor. We have branches in many towns.")
player("And what do you do?")
npc("We will look after your items and money for you. Leave your valuables with us if you want to keep them safe.")
goto("main options")
label("jade's employment history")
npc(ChatAnim.FRIENDLY, "Oh, ever since the Guild opened. I like it here.")
player(ChatAnim.ASKING, "Why's that?")
npc(ChatAnim.FRIENDLY, "Well... what with all these warriors around, there's not much chance of my bank being robbed, is there?")
goto("nowhere")
label("is the bounty hunter banker afraid")
npc(ChatAnim.NEUTRAL, "While the Wilderness is quite a dangerous place, The Bank of Gielinor offers us - roving bankers - extraordinary benefits for our hard work in hazardous environments.")
npc(ChatAnim.NEUTRAL, "This allows us to provide our services to customers regardless of their current whereabouts. Our desire to serve is stronger than our fear of the Wilderness.")
goto("nowhere")
}
}
override fun getIds(): IntArray = NPC_IDS
} }

View file

@ -15,7 +15,7 @@ public final class GuardNPC extends AbstractNPC {
/** /**
* The NPC ids of NPCs using this plugin. * The NPC ids of NPCs using this plugin.
*/ */
private static final int[] ID = { 9, 32, 163, 164, 196, 197, 206, 253, 254, 255, 256, 274, 275, 296, 298, 299, 447, 448, 449, 489, 609, 678, 799, 837, 842, 862, 870, 877, 917, 1200, 1203, 1204, 1317, 1710, 1711, 1712, 2073, 2074, 2134, 2135, 2136, 2236, 2571, 2699, 2700, 2701, 2702, 2703, 3228, 3229, 3230, 3231, 3232, 3233, 3241, 3407, 3408, 3715, 4257, 4258, 4259, 4260, 4307, 4308, 4309, 4310, 4311, 4336, 4375, 4603, 4604, 4605, 4606, 5800, 5801, 5919, 5920 }; private static final int[] ID = { 9, 32, 163, 164, 196, 197, 206, 253, 254, 255, 256, 274, 275, 296, 298, 299, 447, 448, 449, 609, 678, 799, 837, 842, 862, 870, 877, 917, 1200, 1203, 1204, 1317, 1710, 1711, 1712, 2073, 2074, 2134, 2135, 2136, 2236, 2571, 2699, 2700, 2701, 2702, 2703, 3228, 3229, 3230, 3231, 3232, 3233, 3241, 3407, 3408, 3715, 4257, 4258, 4259, 4260, 4307, 4308, 4309, 4310, 4311, 4336, 4375, 4603, 4604, 4605, 4606, 5800, 5801, 5919, 5920 };
/** /**
* Constructs a new {@code GuardNPC} {@code Object}. * Constructs a new {@code GuardNPC} {@code Object}.

View file

@ -12,6 +12,7 @@ import core.game.system.timer.impl.AntiMacro
import core.game.worldevents.holiday.HolidayRandomEventNPC import core.game.worldevents.holiday.HolidayRandomEventNPC
import core.game.worldevents.holiday.HolidayRandomEvents import core.game.worldevents.holiday.HolidayRandomEvents
import core.game.worldevents.holiday.HolidayRandoms import core.game.worldevents.holiday.HolidayRandoms
import org.rs09.consts.NPCs
/** /**
* Handles the NPC talk-to option. * Handles the NPC talk-to option.
@ -37,11 +38,18 @@ class NPCTalkListener : InteractionListener {
val npc = node.asNpc() val npc = node.asNpc()
if(RandomEvents.randomIDs.contains(node.id)){ if(RandomEvents.randomIDs.contains(node.id)){
if(AntiMacro.getEventNpc(player) == null || AntiMacro.getEventNpc(player) != node.asNpc() || AntiMacro.getEventNpc(player)?.finalized == true) { if(AntiMacro.getEventNpc(player) == null || AntiMacro.getEventNpc(player) != node.asNpc() || AntiMacro.getEventNpc(player)?.finalized == true) {
player.sendMessage("They aren't interested in talking to you.") // Why the fuck is this here of all places? Now look at what you've made me do:
if (npc.id == NPCs.SANDWICH_LADY_3117) {
// https://www.youtube.com/watch?v=ek8r3ZS929E
player.dialogueInterpreter.sendDialogue("The sandwich lady doesn't seem interested in selling you any", "refreshments.")
} else {
sendMessage(player, "They aren't interested in talking to you.")
}
} else { } else {
AntiMacro.getEventNpc(player)?.talkTo(node.asNpc()) AntiMacro.getEventNpc(player)?.talkTo(node.asNpc())
} }
return@on true return@on true
//TODO bring sanity here
} }
if (HolidayRandomEvents.holidayRandomIDs.contains(node.id) && node is HolidayRandomEventNPC) { if (HolidayRandomEvents.holidayRandomIDs.contains(node.id) && node is HolidayRandomEventNPC) {
if(HolidayRandoms.getEventNpc(player) == null || HolidayRandoms.getEventNpc(player) != node.asNpc() || HolidayRandoms.getEventNpc(player)?.finalized == true) { if(HolidayRandoms.getEventNpc(player) == null || HolidayRandoms.getEventNpc(player) != node.asNpc() || HolidayRandoms.getEventNpc(player)?.finalized == true) {
@ -79,4 +87,4 @@ class NPCTalkListener : InteractionListener {
return@on player.dialogueInterpreter.open(npc.id, npc) return@on player.dialogueInterpreter.open(npc.id, npc)
} }
} }
} }

View file

@ -4,8 +4,7 @@ import core.game.dialogue.DialoguePlugin
import content.data.RepairItem import content.data.RepairItem
import core.game.interaction.NodeUsageEvent import core.game.interaction.NodeUsageEvent
import core.game.interaction.UseWithHandler import core.game.interaction.UseWithHandler
import content.region.misthalin.lumbridge.dialogue.BobDialogue.BarrowsEquipment import content.global.handlers.item.equipment.BarrowsEquipment
import content.region.misthalin.lumbridge.dialogue.BobDialogue.BarrowsEquipment.BarrowsFullEquipment
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.node.item.Item import core.game.node.item.Item
@ -14,8 +13,10 @@ import core.plugin.Plugin
import kotlin.math.ceil import kotlin.math.ceil
import org.rs09.consts.Items import org.rs09.consts.Items
private val ALL_REPAIRABLE_ITEM_IDS = (RepairItem.repairableItemIds + BarrowsEquipment.getAllRepairableBarrowsIds()).toIntArray()
@Initializable @Initializable
class ArmourStand : UseWithHandler(494, 468, 496, 470, 498, 472, 500, 502, 474, 504, 476, 506, 478, 6741, 4856, 4857, 4858, 4859, 4860, 4862, 4863, 4864, 4865, 4866, 4868, 4869, 4870, 4871, 4872, 4874, 4875, 4876, 4877, 4878, 4880, 4881, 4882, 4883, 4884, 4886, 4887, 4888, 4889, 4890, 4892, 4893, 4894, 4895, 4896, 4898, 4899, 4900, 4901, 4902, 4904, 4905, 4906, 4907, 4908, 4910, 4911, 4912, 4913, 4914, 4916, 4917, 4918, 4919, 4920, 4922, 4923, 4924, 4925, 4926, 4928, 4929, 4930, 4931, 4932, 4934, 4935, 4936, 4937, 4938, 4940, 4941, 4942, 4943, 4944, 4946, 4947, 4948, 4949, 4950, 4952, 4953, 4954, 4955, 4956, 4958, 4959, 4960, 4961, 4962, 4964, 4965, 4966, 4967, 4968, 4970, 4971, 4972, 4973, 4974, 4976, 4977, 4978, 4979, 4980, 4982, 4983, 4984, 4985, 4986, 4988, 4989, 4990, 4991, 4992, 4994, 4995, 4996, 4997, 4998){ class ArmourStand : UseWithHandler(*ALL_REPAIRABLE_ITEM_IDS) {
override fun newInstance(arg: Any?): Plugin<Any> { override fun newInstance(arg: Any?): Plugin<Any> {
addHandler(13715, OBJECT_TYPE, this) addHandler(13715, OBJECT_TYPE, this)
return this return this
@ -24,76 +25,90 @@ class ArmourStand : UseWithHandler(494, 468, 496, 470, 498, 472, 500, 502, 474,
override fun handle(event: NodeUsageEvent?): Boolean { override fun handle(event: NodeUsageEvent?): Boolean {
event ?: return false event ?: return false
val player = event.player val player = event.player
val repairItem = RepairItem.forId(event.used.id) val usedItem = event.used.asItem()
var baseCost = 0.0 var baseCost = 0
var product: Item? = null var product: Item? = null
if(repairItem != null){ val repairItem = RepairItem.forId(usedItem.id)
baseCost = repairItem.cost * 1.0 val barrowsDef = BarrowsEquipment.getDefinition(usedItem.id)
product = repairItem.product
} else if(BarrowsEquipment.isBarrowsItem(event.used.id)){
//Begin terrible code thanks to Vexia
val type = BarrowsEquipment.formatedName(event.used.id)
val single = BarrowsEquipment.getSingleName(type)
val equipment = BarrowsEquipment.getEquipmentType(type)
val newString = type.toLowerCase().replace(single, "").trim { it <= ' ' }.replace("'s", "")
val newewString = StringBuilder()
newewString.append(newString).append(" $equipment")
val fullequip = BarrowsFullEquipment.forName(newewString.toString())
baseCost = BarrowsEquipment.getFormatedCost(equipment,event.used.asItem()) * 1.0
product = fullequip.full
//End terrible code thanks to Vexia
}
if((repairItem == null && baseCost == 0.0)){ if (repairItem != null) {
baseCost = repairItem.cost
product = repairItem.product
} else if (barrowsDef != null) {
if (BarrowsEquipment.isFullyRepaired(event.used.id)) {
player.sendMessage("That item can't be repaired.")
return true
}
baseCost = BarrowsEquipment.getRepairCost(usedItem)
product = Item(barrowsDef.repairedId)
} else {
player.sendMessage("That item can't be repaired.") player.sendMessage("That item can't be repaired.")
return true return true
} }
val cost: Int = ceil(((100.0 - (player.skills.getLevel(Skills.SMITHING) / 2.0) ) / 100.0) * baseCost).toInt() val discountMultiplier = (100.0 - (player.skills.getLevel(Skills.SMITHING) / 2.0)) / 100.0
val cost = ceil(discountMultiplier * baseCost).toInt()
player.dialogueInterpreter.open(58824213,event.used,cost,product)
player.dialogueInterpreter.open(58824213,usedItem, cost, product)
return true return true
} }
@Initializable @Initializable
class RepairDialogue(player: Player? = null) : DialoguePlugin(player){ class RepairDialogue(player: Player? = null) : DialoguePlugin(player) {
override fun newInstance(player: Player?): DialoguePlugin {
return RepairDialogue(player) private var item: Item? = null
} private var cost: Int = 0
var item: Item? = null private var product: Item? = null
var cost: Int = 0
var product: Item? = null override fun newInstance(player: Player?): DialoguePlugin = RepairDialogue(player)
override fun open(vararg args: Any?): Boolean { override fun open(vararg args: Any?): Boolean {
item = args[0] as Item item = args[0] as Item
cost = args[1] as Int cost = args[1] as Int
product = args[2] as Item product = args[2] as Item
player.dialogueInterpreter.sendDialogue("Would you like to repair your ${(item as Item).name.toLowerCase()}","for $cost gp?")
val itemName = item?.name?.lowercase() ?: "item"
player.dialogueInterpreter.sendDialogue(
"Would you like to repair your $itemName",
"for $cost gp?"
)
stage = 0 stage = 0
return true return true
} }
override fun handle(interfaceId: Int, buttonId: Int): Boolean { override fun handle(interfaceId: Int, buttonId: Int): Boolean {
item ?: return false val currentItem = item ?: return false
product ?: return false val currentProduct = product ?: return false
when(stage){
0 -> options("Yes, please","No, thanks").also{stage++} when (stage) {
1 -> when(buttonId){ 0 -> {
1 -> exchangeItems(item as Item,cost,product as Item).also { end() } options("Yes, please", "No, thanks")
stage++
}
1 -> when (buttonId) {
1 -> {
exchangeItems(currentItem, cost, currentProduct)
end()
}
2 -> end() 2 -> end()
} }
} }
return true return true
} }
fun exchangeItems(item: Item, cost: Int, product: Item) { private fun exchangeItems(item: Item, cost: Int, product: Item) {
val coins = Item(Items.COINS_995, cost) val coins = Item(Items.COINS_995, cost)
if (player.inventory.containsItem(coins) && player.inventory.containsItem(item)) { if (player.inventory.containsItem(coins) && player.inventory.containsItem(item)) {
player.inventory.remove(item, coins) if (player.inventory.remove(item, coins)) {
player.inventory.add(product) if (player.inventory.add(product)) {
player.sendMessage("You repair your ${product.name.toLowerCase()} for $cost.") val costText = if (cost > 0) "${cost}gp" else "free"
player.sendMessage("You repair your ${product.name.lowercase()} for $costText.")
return
}
}
player.sendMessage("Report this to an administrator!")
} else { } else {
player.sendMessage("You can't afford that.") player.sendMessage("You can't afford that.")
} }

View file

@ -1,17 +1,22 @@
package content.global.skill.cooking package content.global.skill.cooking
import core.api.amountInInventory import content.region.misc.tutisland.handlers.TutorialStage
import core.api.*
import core.game.interaction.IntType import core.game.interaction.IntType
import core.game.interaction.InteractionListener import core.game.interaction.InteractionListener
import core.game.interaction.QueueStrength
import core.game.node.entity.impl.Animator
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.item.Item import core.game.node.item.Item
import core.game.node.scenery.Scenery import core.game.node.scenery.Scenery
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.Items.BREAD_DOUGH_2307 import org.rs09.consts.Items.BREAD_DOUGH_2307
import org.rs09.consts.Items.RAW_BEAR_MEAT_2136 import org.rs09.consts.Items.RAW_BEAR_MEAT_2136
import org.rs09.consts.Items.RAW_BEEF_2132 import org.rs09.consts.Items.RAW_BEEF_2132
import org.rs09.consts.Items.SEAWEED_401 import org.rs09.consts.Items.SEAWEED_401
import org.rs09.consts.Items.UNCOOKED_CAKE_1889 import org.rs09.consts.Items.UNCOOKED_CAKE_1889
import org.rs09.consts.Sounds
/** /**
* @author Ceikry * @author Ceikry
@ -32,8 +37,36 @@ class CookingRewrite : InteractionListener {
} }
override fun defineListeners() { override fun defineListeners() {
onUseWith(IntType.SCENERY,RAW_FOODS, *COOKING_OBJs){ player, used, with -> onUseWith(IntType.SCENERY,RAW_FOODS, *COOKING_OBJs){ player, used, with ->
if (!getAttribute(player, "/save:tutorial:complete", false)) {
// On tutorial island, we don't want to show the cook-x menu and we always want to burn our first shrimp
// This requirement of a simplified and predictable cooking system means that, for the second time, I have to reinvent the wheel of cooking
// (We only need to care about shrimp here, the tutorial island range is special and has its own separate listener)
queueScript(player, 0, QueueStrength.WEAK) { stage ->
if (stage == 0) {
val FIRE_ANIMATION = Animation(897, Animator.Priority.HIGH)
lock(player, FIRE_ANIMATION.duration)
lockInteractions(player, FIRE_ANIMATION.duration)
animate(player, FIRE_ANIMATION)
playAudio(player, Sounds.FRY_2577)
return@queueScript delayScript(player, FIRE_ANIMATION.duration)
}
val tutStage = getAttribute(player, "/save:tutorial:stage", 0)
if (tutStage < 15) {
replaceSlot(player, used.asItem().slot, Item(Items.BURNT_SHRIMP_7954), used.asItem())
setAttribute(player, "tutorial:stage", 15)
TutorialStage.load(player, 15)
} else {
replaceSlot(player, used.asItem().slot, Item(Items.SHRIMPS_315), used.asItem())
if (tutStage == 15) {
setAttribute(player, "tutorial:stage", 16)
TutorialStage.load(player, 16)
}
}
return@queueScript stopExecuting(player)
}
return@onUseWith true
}
val item = used.asItem() val item = used.asItem()
val obj = with.asScenery() val obj = with.asScenery()
val range = obj.name.toLowerCase().contains("range") val range = obj.name.toLowerCase().contains("range")
@ -66,7 +99,7 @@ class CookingRewrite : InteractionListener {
} }
companion object { companion object {
val COOKING_OBJs = intArrayOf(24313,21302, 13528, 13529, 13533, 13531, 13536, 13539, 13542, 2728, 2729, 2730, 2731, 2732, 2859, 3038, 3039, 3769, 3775, 4265, 4266, 5249, 5499, 5631, 5632, 5981, 9682, 10433, 11404, 11405, 11406, 12102, 12796, 13337, 13881, 14169, 14919, 15156, 20000, 20001, 21620, 21792, 22713, 22714, 23046, 24283, 24284, 25155, 25156, 25465, 25730, 27297, 29139, 30017, 32099, 33500, 34495, 34546, 36973, 37597, 37629, 37726, 114, 4172, 5275, 8750, 16893, 22154, 34410, 34565, 114, 9085, 9086, 9087, 12269, 15398, 25440, 25441, 2724, 2725, 2726, 4618, 4650, 5165, 6093, 6094, 6095, 6096, 8712, 9374, 9439, 9440, 9441, 10824, 17640, 17641, 17642, 17643, 18039, 18170, 21795, 24285, 24329, 27251, 33498, 35449, 36815, 36816, 37426, 40110, 10377) val COOKING_OBJs = intArrayOf(24313,21302, 13528, 13529, 13533, 13531, 13536, 13539, 13542, 2728, 2729, 2730, 2731, 2732, 2859, 3038, 3769, 3775, 4265, 4266, 5249, 5499, 5631, 5632, 5981, 9682, 10433, 11404, 11405, 11406, 12102, 12796, 13337, 13881, 14169, 14919, 15156, 20000, 20001, 21620, 21792, 22713, 22714, 23046, 24283, 24284, 25155, 25156, 25465, 25730, 27297, 29139, 30017, 32099, 33500, 34495, 34546, 36973, 37597, 37629, 37726, 114, 4172, 5275, 8750, 16893, 22154, 34410, 34565, 114, 9085, 9086, 9087, 12269, 15398, 25440, 25441, 2724, 2725, 2726, 4618, 4650, 5165, 6093, 6094, 6095, 6096, 8712, 9374, 9439, 9440, 9441, 10824, 17640, 17641, 17642, 17643, 18039, 18170, 21795, 24285, 24329, 27251, 33498, 35449, 36815, 36816, 37426, 40110, 10377)
@JvmStatic @JvmStatic
fun cook(player: Player, `object`: Scenery?, initial: Int, product: Int, amount: Int) { fun cook(player: Player, `object`: Scenery?, initial: Int, product: Int, amount: Int) {

View file

@ -1,5 +1,6 @@
package content.global.skill.cooking package content.global.skill.cooking
import content.region.misc.tutisland.handlers.TutorialStage
import core.api.* import core.api.*
import core.game.event.ResourceProducedEvent import core.game.event.ResourceProducedEvent
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
@ -24,7 +25,16 @@ class DoughMakingListener : InteractionListener {
FULL_WATER_CONTAINERS_TO_EMPTY_CONTAINERS.keys.toIntArray(), FULL_WATER_CONTAINERS_TO_EMPTY_CONTAINERS.keys.toIntArray(),
Items.POT_OF_FLOUR_1933 Items.POT_OF_FLOUR_1933
) { player, waterContainer, flourContainer -> ) { player, waterContainer, flourContainer ->
openDialogue(player, DoughMakeDialogue(waterContainer.asItem(), flourContainer.asItem())) if (getAttribute(player, "/save:tutorial:complete", false)) {
openDialogue(player, DoughMakeDialogue(waterContainer.asItem(), flourContainer.asItem()))
return@onUseWith true
}
// Continue the tutorial
replaceSlot(player, waterContainer.asItem().slot, Item(Items.BUCKET_1925), waterContainer.asItem())
replaceSlot(player, flourContainer.asItem().slot, Item(Items.EMPTY_POT_1931), flourContainer.asItem())
addItemOrDrop(player, Items.BREAD_DOUGH_2307)
setAttribute(player, "tutorial:stage", 20)
TutorialStage.load(player, 20)
return@onUseWith true return@onUseWith true
} }
} }
@ -73,7 +83,7 @@ class DoughMakingListener : InteractionListener {
sendMessage( sendMessage(
player!!, player!!,
"You mix the flower and the water to make some ${selectedDoughProduct.itemName.toLowerCase()}." "You mix the flour and the water to make some ${selectedDoughProduct.itemName.toLowerCase()}."
) )
} }
} else { } else {

View file

@ -0,0 +1,18 @@
package content.global.skill.cooking
import org.rs09.consts.Items
import core.api.replaceSlot
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.item.Item
class NettleTeaListener : InteractionListener {
override fun defineListeners() {
onUseWith(IntType.ITEM, Items.EMPTY_CUP_1980, Items.NETTLE_TEA_4239) { player, used, with ->
replaceSlot(player, with.asItem().slot, Item(Items.BOWL_1923), with.asItem())
replaceSlot(player, used.asItem().slot, Item(Items.CUP_OF_TEA_4242), used.asItem())
return@onUseWith true
}
}
}

View file

@ -1,61 +0,0 @@
package content.global.skill.cooking;
import core.game.interaction.NodeUsageEvent;
import core.game.interaction.UseWithHandler;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.plugin.Initializable;
import core.plugin.Plugin;
/**
* Represents the plugin used to create nettle tea in a cup.
* @author 'Vexia
* @version 1.0
*/
@Initializable
public final class NettleTeaPlugin extends UseWithHandler {
/**
* Represents the empty cup item.
*/
private static final Item EMPTY_CUP = new Item(1980, 1);
/**
* Represents the nettle tea item.
*/
private static final Item NETTLE_TEA = new Item(4239, 1);
/**
* Represents the bowl item.
*/
private static final Item BOWL = new Item(1923);
/**
* Represents the cup of tea item.
*/
private static final Item CUP_OF_TEA = new Item(4242, 1);
/**
* Constructs a new {@code NettleTeaPlugin} {@code Object}.
*/
public NettleTeaPlugin() {
super(1980);
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
addHandler(4239, ITEM_TYPE, this);
return this;
}
@Override
public boolean handle(NodeUsageEvent event) {
final Player player = event.getPlayer();
if (player.getInventory().remove(EMPTY_CUP) && player.getInventory().remove(NETTLE_TEA)) {
player.getInventory().add(BOWL);
player.getInventory().add(CUP_OF_TEA);
}
return true;
}
}

View file

@ -0,0 +1,18 @@
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
}
}
}

View file

@ -1,35 +0,0 @@
package content.global.skill.cooking;
import core.game.interaction.NodeUsageEvent;
import core.game.interaction.UseWithHandler;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.plugin.Initializable;
import core.plugin.Plugin;
/**
* @author Adam
*/
@Initializable
public class NettleWaterPlugin extends UseWithHandler {
public NettleWaterPlugin() {
super(1921);
}
@Override
public boolean handle(NodeUsageEvent event) {
final Player player = event.getPlayer();
player.getInventory().remove(new Item(1921, 1));
player.getInventory().remove(new Item(4241, 1));
player.getInventory().add(new Item(4237, 1));
return true;
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
addHandler(4241, ITEM_TYPE, this);
return this;
}
}

View file

@ -56,7 +56,7 @@ public final class PotteryPlugin extends UseWithHandler {
@Override @Override
public boolean handle(final NodeUsageEvent event) { public boolean handle(final NodeUsageEvent event) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, (Object[]) getPottery(false)) { new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, getPottery(false)) {
@Override @Override
public void create(final int amount, int index) { public void create(final int amount, int index) {
@ -145,7 +145,7 @@ public final class PotteryPlugin extends UseWithHandler {
* @return the dialogue handler. * @return the dialogue handler.
*/ */
public SkillDialogueHandler getSkillHandler(final Player player) { public SkillDialogueHandler getSkillHandler(final Player player) {
return new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, (Object[]) getPottery(true)) { return new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, getPottery(true)) {
@Override @Override
public void create(final int amount, final int index) { public void create(final int amount, final int index) {

View file

@ -34,7 +34,7 @@ public class SnakeSkinPlugin extends UseWithHandler {
@Override @Override
public boolean handle(final NodeUsageEvent event) { public boolean handle(final NodeUsageEvent event) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, (Object[]) getSkins()) { new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, getSkins()) {
@Override @Override
public void create(final int amount, int index) { public void create(final int amount, int index) {

View file

@ -1,12 +1,15 @@
package content.global.skill.crafting.lightsources package content.global.skill.crafting.lightsources
import content.data.LightSource
import core.api.log import core.api.log
import core.api.*
import core.cache.def.impl.ItemDefinition import core.cache.def.impl.ItemDefinition
import core.game.container.Container import core.game.container.Container
import core.game.interaction.OptionHandler import core.game.interaction.OptionHandler
import core.game.node.Node import core.game.node.Node
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.item.Item import core.game.node.item.Item
import core.game.world.map.Location
import core.tools.SystemLogger import core.tools.SystemLogger
import core.plugin.Initializable import core.plugin.Initializable
import core.plugin.Plugin import core.plugin.Plugin
@ -32,6 +35,12 @@ class LightSourceExtinguisher : OptionHandler(){
lightSource ?: return false.also { log(this::class.java, Log.WARN, "UNHANDLED EXTINGUISH OPTION: ID = ${node.id}") } lightSource ?: return false.also { log(this::class.java, Log.WARN, "UNHANDLED EXTINGUISH OPTION: ID = ${node.id}") }
// For Temple of Ikov - if you are in the dark basement, do not let light source extinguish.
if(player.location.isInRegion(10648)) {
sendMessage(player, "Extinguishing the " + LightSource.getActiveLightSource(player).product.name.lowercase() + " would leave you without a light source.")
return true
}
player.inventory.replace(node.asItem(), Item(lightSource.fullID)) player.inventory.replace(node.asItem(), Item(lightSource.fullID))
return true return true
} }

View file

@ -1,12 +1,16 @@
package content.global.skill.crafting.lightsources package content.global.skill.crafting.lightsources
import core.api.*
import core.api.teleport
import core.game.container.Container import core.game.container.Container
import core.game.event.LitLightSourceEvent import core.game.event.LitLightSourceEvent
import core.game.interaction.NodeUsageEvent import core.game.interaction.NodeUsageEvent
import core.game.interaction.UseWithHandler import core.game.interaction.UseWithHandler
import core.game.node.entity.Entity
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.node.item.Item import core.game.node.item.Item
import core.game.world.map.Location
import core.plugin.Initializable import core.plugin.Initializable
import core.plugin.Plugin import core.plugin.Plugin
@ -72,6 +76,14 @@ class LightSourceLighter : UseWithHandler(590,36,38){
return true return true
} }
// For Temple of Ikov - if you are in the dark basement and light a light source, switch to the light basement.
// For the listener that covers the firemaking cape perk, see content.global.skill.skillcapeperks.SkillcapePerks.kt
if(event.player.location.isInRegion(10648) && event.player.location.withinDistance(Location(2639,9738,0), 8)) {
teleport(event.player, Location.create(event.player.getLocation().getX(), event.player.getLocation().getY() + 23, event.player.getLocation().getZ()))
closeDialogue(event.player)
// Dark basement is region 10648, min 2639 9738 0, max 2643 9744 0. Add 23 to the Y loc to tele to light basement
}
if(!light(event.player,used,lightSource)){ if(!light(event.player,used,lightSource)){
event.player.sendMessage("You need a Firemaking level of at least ${lightSource.levelRequired} to light this.") event.player.sendMessage("You need a Firemaking level of at least ${lightSource.levelRequired} to light this.")
} }

View file

@ -27,7 +27,7 @@ enum class SilverProduct(
val strungId: Int val strungId: Int
) { ) {
HOLY(BUTTON_UNBLESSED, Items.HOLY_MOULD_1599, Items.UNSTRUNG_SYMBOL_1714, 1, 16, 50.0, Items.UNBLESSED_SYMBOL_1716), HOLY(BUTTON_UNBLESSED, Items.HOLY_MOULD_1599, Items.UNSTRUNG_SYMBOL_1714, 1, 16, 50.0, Items.UNBLESSED_SYMBOL_1716),
UNHOLY(BUTTON_UNHOLY, Items.UNHOLY_MOULD_1594, Items.UNSTRUNG_EMBLEM_1720, 1, 17, 50.0, Items.UNHOLY_SYMBOL_1724), UNHOLY(BUTTON_UNHOLY, Items.UNHOLY_MOULD_1594, Items.UNSTRUNG_EMBLEM_1720, 1, 17, 50.0, Items.UNPOWERED_SYMBOL_1722),
SICKLE(BUTTON_SICKLE, Items.SICKLE_MOULD_2976, Items.SILVER_SICKLE_2961, 1, 18, 50.0, -1), SICKLE(BUTTON_SICKLE, Items.SICKLE_MOULD_2976, Items.SILVER_SICKLE_2961, 1, 18, 50.0, -1),
TIARA(BUTTON_TIARA, Items.TIARA_MOULD_5523, Items.TIARA_5525, 1, 23, 52.5, -1), TIARA(BUTTON_TIARA, Items.TIARA_MOULD_5523, Items.TIARA_5525, 1, 23, 52.5, -1),
SILVTHRIL_CHAIN(BUTTON_SILVTHRIL_CHAIN, Items.CHAIN_LINK_MOULD_13153, Items.SILVTHRIL_CHAIN_13154, 1, 47, 100.0, -1), SILVTHRIL_CHAIN(BUTTON_SILVTHRIL_CHAIN, Items.CHAIN_LINK_MOULD_13153, Items.SILVTHRIL_CHAIN_13154, 1, 47, 100.0, -1),

View file

@ -5,6 +5,7 @@ import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler import core.game.interaction.OptionHandler
import core.game.node.Node import core.game.node.Node
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.diary.DiaryType
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.plugin.Initializable import core.plugin.Initializable
import core.plugin.Plugin import core.plugin.Plugin
@ -42,6 +43,9 @@ class HealthChecker : OptionHandler() {
} }
PatchType.FRUIT_TREE_PATCH -> { PatchType.FRUIT_TREE_PATCH -> {
patch.setCurrentState(patch.getCurrentState() - 14) patch.setCurrentState(patch.getCurrentState() - 14)
if (fPatch == FarmingPatch.BRIMHAVEN_FRUIT_TREE) {
player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 12)
}
sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.") sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.")
} }
PatchType.SPIRIT_TREE_PATCH -> { PatchType.SPIRIT_TREE_PATCH -> {

View file

@ -373,8 +373,6 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
setCurrentState(getCurrentState() + 1) setCurrentState(getCurrentState() + 1)
isWatered = false isWatered = false
} }
regrowIfTreeStump()
} }
fun regrowIfTreeStump() { fun regrowIfTreeStump() {

View file

@ -0,0 +1,5 @@
package content.global.skill.farming
class Stump(val varbit: Int, val TTL: Long) {
}

View file

@ -0,0 +1,59 @@
package content.global.skill.farming.timers
import core.game.node.entity.Entity
import core.game.system.timer.*
import core.game.node.entity.player.Player
import content.global.skill.farming.*
import core.tools.ticksToSeconds
import java.util.concurrent.TimeUnit
import org.json.simple.*
class StumpGrowth : PersistTimer (1, "farming:stump", isSoft = true) {
val stumps = ArrayList<Stump>()
lateinit var player: Player
fun addStump(varbit: Int, ttl: Int){
stumps.add(
Stump(
varbit,
System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(ticksToSeconds(ttl).toLong())
)
)
}
override fun onRegister (entity: Entity) {
player = (entity as? Player)!!
}
override fun run (entity: Entity) : Boolean {
val removeList = ArrayList<Stump>()
for (stump in stumps) {
if (System.currentTimeMillis() > stump.TTL) {
FarmingPatch.patches[stump.varbit]?.getPatchFor(player)?.regrowIfTreeStump()
removeList.add(stump)
}
}
stumps.removeAll(removeList)
return stumps.isNotEmpty()
}
override fun save(root: JSONObject, entity: Entity) {
val stumpArray = JSONArray()
for(s in stumps){
val seed = JSONObject()
seed["patch"] = s.varbit
seed["ttl"] = s.TTL
stumpArray.add(seed)
}
root.put("stumps",stumpArray)
}
override fun parse(root: JSONObject, entity: Entity) {
(root["stumps"] as JSONArray).forEach {
val s = it as JSONObject
val id = s["patch"].toString().toInt()
val ttl = s["ttl"].toString().toLong()
stumps.add(Stump(id,ttl))
}
}
}

View file

@ -0,0 +1,8 @@
package content.global.skill.fletching
object AchievementDiaryAttributeKeys {
/**
* This attribute signifies that the fletch of the magic shortbow is complete which is the first stage of getting the achievement
*/
const val FLETCHED_UNSTRUNG_MAGIC_SHORTBOW = "diary:seers:fletch-magic-short-bow"
}

View file

@ -0,0 +1,16 @@
package content.global.skill.fletching
import org.rs09.consts.Items
object Feathers {
val all = intArrayOf(
Items.FEATHER_314,
Items.STRIPY_FEATHER_10087,
Items.RED_FEATHER_10088,
Items.BLUE_FEATHER_10089,
Items.YELLOW_FEATHER_10090,
Items.ORANGE_FEATHER_10091
)
const val STANDARD = Items.FEATHER_314
}

View file

@ -1,355 +0,0 @@
package content.global.skill.fletching;
import core.game.node.item.Item;
import core.game.world.update.flag.context.Animation;
import java.util.HashMap;
public class Fletching {
public static HashMap<Integer, FletchingItems[]>logMap = new HashMap<>();
public static HashMap<Integer, Bolts> boltMap = new HashMap<>();
public static HashMap<Integer, Darts> dartMap = new HashMap<>();
public static HashMap<Integer, ArrowHeads> arrowHeadMap = new HashMap<>();
public static HashMap<Integer, GemBolts> gemMap = new HashMap<>();
public static HashMap<Integer, GemBolts> tipMap = new HashMap<>();
public static HashMap<Integer, String> stringMap = new HashMap<>();
public static HashMap<Integer, Limb> limbMap = new HashMap<>();
static{
Items[] itemsArray = Items.values();
int thisLength = itemsArray.length;
for(int x = 0; x < thisLength; x++){
Items item = itemsArray[x];
logMap.putIfAbsent(item.id, item.items);
}
Bolts[] boltArray = Bolts.values();
thisLength = boltArray.length;
for(int x = 0; x < thisLength; x++){
Bolts bolt = boltArray[x];
boltMap.putIfAbsent(bolt.unfinished,bolt);
}
Darts[] dartArray = Darts.values();
thisLength = dartArray.length;
for(int x = 0; x < thisLength; x++){
Darts dart = dartArray[x];
dartMap.putIfAbsent(dart.unfinished,dart);
}
ArrowHeads[] ahArray = ArrowHeads.values();
thisLength = ahArray.length;
for(int x = 0; x < thisLength; x++){
ArrowHeads arrowhead = ahArray[x];
arrowHeadMap.putIfAbsent(arrowhead.unfinished,arrowhead);
}
GemBolts[] gbArray = GemBolts.values();
thisLength = gbArray.length;
for(int x = 0; x < thisLength; x++){
GemBolts gem = gbArray[x];
gemMap.putIfAbsent(gem.gem,gem);
tipMap.putIfAbsent(gem.tip,gem);
}
String[] stringArray = String.values();
thisLength = stringArray.length;
for(int x = 0; x < thisLength; x++){
String bow = stringArray[x];
stringMap.putIfAbsent(bow.unfinished,bow);
}
Limb[] limbsArray = Limb.values();
thisLength = limbsArray.length;
for(int x = 0; x < thisLength; x++){
Limb limb = limbsArray[x];
limbMap.putIfAbsent(limb.stock, limb);
}
}
public static FletchingItems[] getEntries(int id){
return logMap.get(id);
}
public static boolean isLog(int id){
return logMap.get(id) != null;
}
public static boolean isBolt(int id){
return boltMap.get(id) != null;
}
public static boolean isDart(int id){
return dartMap.get(id) != null;
}
public static boolean isArrowHead(int id){
return arrowHeadMap.get(id) != null;
}
public static boolean isGemTip(int id){
return tipMap.get(id) != null;
}
public static Item[] getItems(int id){
FletchingItems[] entry = getEntries(id);
Item items[] = {};
switch(entry.length){
case 1:
items = new Item[] {new Item(entry[0].id)};
break;
case 2:
items = new Item[] {new Item(entry[0].id), new Item(entry[1].id)};
break;
case 3:
items = new Item[] {new Item(entry[0].id), new Item(entry[1].id), new Item(entry[2].id)};
break;
case 4:
items = new Item[] {new Item(entry[0].id), new Item(entry[1].id), new Item(entry[2].id), new Item(entry[3].id)};
break;
}
return items;
}
public enum Limb {
WOODEN_STOCK(9440,9420,9454,9, 12, new Animation(4436)),
OAK_STOCK(9442,9422,9456,24, 32, new Animation(4437)),
WILLOW_STOCK(9444,9423,9457,39, 44, new Animation(4438)),
TEAK_STOCK(9446,9425,9459,46, 54, new Animation(4439)),
MAPLE_STOCK(9448,9427,9461,54, 64, new Animation(4440)),
MAHOGANY_STOCK(9450,9429,9463,61, 82, new Animation(4441)),
YEW_STOCK(9452,9431,9465,69, 100, new Animation(4442));
public int stock, limb, product,level;
public double experience;
public Animation animation;
Limb(int stock, int limb, int product, int level, double experience, Animation animation) {
this.stock = stock;
this.limb = limb;
this.product = product;
this.level = level;
this.experience = experience;
this.animation = animation;
}
}
public enum String{
//Bows
SHORT_BOW((byte) 1,50,841,5, 5, new Animation(6678)),
LONG_BOW((byte) 1,48,839,10, 10, new Animation(6684)),
OAK_SHORTBOW((byte) 1,54,843,20, 16.5, new Animation(6679)),
OAK_LONGBOW((byte) 1,56,845,25, 25, new Animation(6685)),
WILLOW_SHORTBOW((byte) 1,60,849,35, 33.3, new Animation(6680)),
WILLOW_LONGBOW((byte) 1,58,847,40, 41.5, new Animation(6686)),
MAPLE_SHORTBOW((byte) 1,64,853,50, 50, new Animation(6681)),
MAPLE_LONGBOW((byte) 1,62,851,55, 58.3, new Animation(6687)),
YEW_SHORTBOW((byte) 1,68,857,65, 67.5, new Animation(6682)),
YEW_LONGBOW((byte) 1,66,855,70, 75, new Animation(6688)),
MAGIC_SHORTBOW((byte) 1,72,861,80, 83.3, new Animation(6683)),
MAGIC_LONGBOW((byte) 1,70,859,85, 91.5, new Animation(6689)),
OGRE_COMP_BOW((byte) 1,4825,4827,30, 45, new Animation(-1)),
//crossbows
BRONZE_CBOW((byte) 2,9454,9174,9, 6, new Animation(6671)),
BLURITE_CBOW((byte) 2,9456,9176,24, 16, new Animation(6672)),
IRON_CBOW((byte) 2,9457,9177,39, 22, new Animation(6673)),
STEEL_CBOW((byte) 2,9459,9179,46, 27, new Animation(6674)),
MITHIRIL_CBOW((byte) 2,9461,9181,54, 32, new Animation(6675)),
ADAMANT_CBOW((byte) 2,9463,9183,61, 41, new Animation(6676)),
RUNITE_CBOW((byte) 2,9465,9185,69, 50, new Animation(6677));
public int unfinished,product,string,level;
public final double experience;
public final Animation animation;
String(byte indicator, final int unfinished, final int product, final int level, final double experience, final Animation animation) {
this.unfinished = unfinished;
this.product = product;
this.level = level;
this.experience = experience;
this.animation = animation;
switch(indicator & 0xFF){
case 1:
this.string = org.rs09.consts.Items.BOW_STRING_1777;
break;
case 2:
this.string = org.rs09.consts.Items.CROSSBOW_STRING_9438;
break;
default:
break;
}
}
}
public enum GemBolts {
OPAL(877, org.rs09.consts.Items.OPAL_1609, 45, 879, 11, 1.6),
PEARL(9140, org.rs09.consts.Items.OYSTER_PEARL_411, 46, 880, 41, 3.2),
PEARLS(9140, org.rs09.consts.Items.OYSTER_PEARLS_413, 46, 880, 41, 3.2),
JADE(9139, org.rs09.consts.Items.JADE_1611, 9187, 9335, 26, 2.4),
RED_TOPAZ(9141, org.rs09.consts.Items.RED_TOPAZ_1613, 9188, 9336, 48, 3.9),
SAPPHIRE(9142, org.rs09.consts.Items.SAPPHIRE_1607, 9189, 9337, 56, 4.7),
EMERALD(9142, org.rs09.consts.Items.EMERALD_1605, 9190, 9338, 58, 5.5),
RUBY(9143, org.rs09.consts.Items.RUBY_1603, 9191, 9339, 63, 6.3),
DIAMOND(9143, org.rs09.consts.Items.DIAMOND_1601, 9192, 9340, 65, 7),
DRAGONSTONE(9144, org.rs09.consts.Items.DRAGONSTONE_1615, 9193, 9341, 71, 8.2),
ONYX(9144, org.rs09.consts.Items.ONYX_6573, 9194, 9342, 73, 9.4);
public int gem,tip,base,product,level;
public double experience;
GemBolts(int base, int gem, int tip, int product, int level, double experience){
this.gem = gem;
this.tip = tip;
this.base = base;
this.product = product;
this.level = level;
this.experience = experience;
}
}
public enum ArrowHeads {
BRONZE_ARROW(39, 882, 1, 1.3),
IRON_ARROW(40, 884, 15, 2.5),
STEEL_ARROW(41, 886, 30, 5),
MITHRIL_ARROW(42, 888, 45, 7.5),
ADAMANT_ARROW(43, 890, 60, 10),
RUNE_ARROW(44, 892, 75, 12.5),
DRAGON_ARROW(11237, 11212, 90, 15),
BROAD_ARROW(13278, 4160, 52, 15);
public int unfinished,finished,level;
public double experience;
ArrowHeads(int unfinished, int finished, int level, double experience){
this.unfinished = unfinished;
this.finished = finished;
this.level = level;
this.experience = experience;
}
public Item getFinished(){
return new Item(finished);
}
public Item getUnfinished(){
return new Item(unfinished);
}
}
public enum Darts{
BRONZE_DART(819, 806, 1, 1.8),
IRON_DART(820, 807, 22, 3.8),
STEEL_DART(821, 808, 37, 7.5),
MITHRIL_DART(822, 809, 52, 11.2),
ADAMANT_DART(823, 810, 67, 15),
RUNE_DART(824, 811, 81, 18.8),
DRAGON_DART(11232, 11230, 95, 25);
public int unfinished, finished, level;
public double experience;
Darts(int unfinished, int finished, int level, double experience){
this.unfinished = unfinished;
this.finished = finished;
this.level = level;
this.experience = experience;
}
public Item getFinished(){
return new Item(finished);
}
public Item getUnfinished(){
return new Item(unfinished);
}
}
public enum Bolts{
BRONZE_BOLT(9375, 877, 9, 0.5),
BLURITE_BOLT(9376, 9139, 24, 1),
IRON_BOLT(9377, 9140, 39, 1.5),
SILVER_BOLT(9382, 9145, 43, 2.5),
STEEL_BOLT(9378, 9141, 46, 3.5),
MITHRIL_BOLT(9379, 9142, 54, 5),
ADAMANTITE_BOLT(9380, 9143, 61, 7),
RUNITE_BOLT(9381, 9144, 69, 10),
BROAD_BOLT(13279, 13280, 55, 3);
public int unfinished, finished, level;
public double experience;
Bolts(int unfinished, int finished, int level, double experience){
this.unfinished = unfinished;
this.finished = finished;
this.level = level;
this.experience = experience;
}
public Item getFinished(){
return new Item(finished);
}
public Item getUnfinished(){
return new Item(unfinished);
}
}
private enum Items{
STANDARD(1511,FletchingItems.ARROW_SHAFT, FletchingItems.SHORT_BOW, FletchingItems.LONG_BOW, FletchingItems.WOODEN_STOCK),
ACHEY(2862, FletchingItems.OGRE_ARROW_SHAFT, FletchingItems.OGRE_COMP_BOW),
OAK(1521, FletchingItems.OAK_SHORTBOW, FletchingItems.OAK_LONGBOW, FletchingItems.OAK_STOCK),
WILLOW(1519, FletchingItems.WILLOW_SHORTBOW, FletchingItems.WILLOW_LONGBOW, FletchingItems.WILLOW_STOCK),
MAPLE(1517, FletchingItems.MAPLE_SHORTOW, FletchingItems.MAPLE_LONGBOW, FletchingItems.MAPLE_STOCK),
YEW(1515, FletchingItems.YEW_SHORTBOW, FletchingItems.YEW_LONGBOW, FletchingItems.YEW_STOCK),
MAGIC(1513, FletchingItems.MAGIC_SHORTBOW, FletchingItems.MAGIC_LONGBOW),
TEAK(6333, FletchingItems.TEAK_STOCK),
MAHOGANY(6332, FletchingItems.MAHOGANY_STOCK);
FletchingItems[] items;
int id;
Items(int id, FletchingItems item_1, FletchingItems item_2, FletchingItems item_3, FletchingItems item_4){
items = new FletchingItems[] {item_1, item_2, item_3, item_4};
this.id = id;
}
Items(int id, FletchingItems item_1, FletchingItems item_2, FletchingItems item_3){
items = new FletchingItems[] {item_1, item_2, item_3};
this.id = id;
}
Items(int id, FletchingItems item_1, FletchingItems item_2){
items = new FletchingItems[] {item_1, item_2};
this.id = id;
}
Items(int id, FletchingItems item_1){
items = new FletchingItems[] {item_1};
this.id = id;
}
}
public enum FletchingItems {
//Standard logs
ARROW_SHAFT(52, 5, 1, 15),
SHORT_BOW(50, 5, 5, 1),
LONG_BOW(48, 10, 10, 1),
WOODEN_STOCK(9440, 6, 9, 1),
//Achey logs
OGRE_ARROW_SHAFT(2864, 6.4, 5, 4),
OGRE_COMP_BOW(4825, 45, 30, 1),
//Oak logs
OAK_SHORTBOW(54, 16.5, 20, 1),
OAK_LONGBOW(56,25,25,1),
OAK_STOCK(9442, 16, 24, 1),
//Willow logs
WILLOW_SHORTBOW(60, 33.3, 35, 1),
WILLOW_LONGBOW(58, 41.5, 40, 1),
WILLOW_STOCK(9444, 22, 39, 1),
//Maple logs
MAPLE_SHORTOW(64, 50, 50, 1),
MAPLE_LONGBOW(62, 58.3, 55, 1),
MAPLE_STOCK(9448, 32, 54, 1),
//Yew logs
YEW_SHORTBOW(68, 67.5, 65, 1),
YEW_LONGBOW(66, 75, 70, 1),
YEW_STOCK(9452, 50, 69, 1),
//Magic logs
MAGIC_SHORTBOW(72, 83.3, 80,1),
MAGIC_LONGBOW(70, 91.5, 85, 1),
//Teak
TEAK_STOCK(9446, 27, 46,1),
//Mahogany
MAHOGANY_STOCK(9450, 41.0, 61, 1);
int id,level,amount,logId;
double experience;
FletchingItems(int id, double experience, int level, int amount){
this.id = id;
this.level = level;
this.amount = amount;
this.experience = experience;
}
public Item getItem(){
return new Item(id);
}
}
}

View file

@ -1,197 +0,0 @@
package content.global.skill.fletching
import content.data.Quests
import content.global.skill.fletching.items.arrow.ArrowHeadPulse
import content.global.skill.fletching.items.arrow.HeadlessArrowPulse
import content.global.skill.fletching.items.arrow.HeadlessOgreArrowPulse
import content.global.skill.fletching.items.bow.StringPulse
import content.global.skill.fletching.items.crossbow.LimbPulse
import core.api.*
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.net.packet.PacketRepository
import core.net.packet.context.ChildPositionContext
import core.net.packet.out.RepositionChild
import org.rs09.consts.Components
import org.rs09.consts.Items
import org.rs09.consts.Items.BLUE_FEATHER_10089
import org.rs09.consts.Items.FEATHER_314
import org.rs09.consts.Items.ORANGE_FEATHER_10091
import org.rs09.consts.Items.RED_FEATHER_10088
import org.rs09.consts.Items.STRIPY_FEATHER_10087
import org.rs09.consts.Items.YELLOW_FEATHER_10090
import core.game.dialogue.SkillDialogueHandler
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import core.game.node.entity.player.Player
class FletchingListeners : InteractionListener {
val LIMBIDs = Fletching.Limb.values().map(Fletching.Limb::limb).toIntArray()
val STOCKIDs = Fletching.Limb.values().map(Fletching.Limb::stock).toIntArray()
val MITHRIL_BOLT = Items.MITHRIL_BOLTS_9142
val MITH_GRAPPLE_TIP = Items.MITH_GRAPPLE_TIP_9416
val ROPE = Items.ROPE_954
val MITH_GRAPPLE = Items.MITH_GRAPPLE_9418
val ROPE_GRAPPLE = Items.MITH_GRAPPLE_9419
val ARROW_SHAFT = Items.ARROW_SHAFT_52
val OGRE_ARROW_SHAFT = Items.OGRE_ARROW_SHAFT_2864
val FLETCHED_SHAFT = Items.HEADLESS_ARROW_53
val FLIGHTED_OGRE_ARROW = Items.FLIGHTED_OGRE_ARROW_2865
val UNFINISHED_ARROWS = Fletching.ArrowHeads.values().map(Fletching.ArrowHeads::unfinished).toIntArray()
val FEATHERS = intArrayOf(FEATHER_314,STRIPY_FEATHER_10087,RED_FEATHER_10088,BLUE_FEATHER_10089,YELLOW_FEATHER_10090,ORANGE_FEATHER_10091)
val UNSTRUNG_BOWS = Fletching.String.values().map(Fletching.String::unfinished).toIntArray()
val STRINGS = intArrayOf(Items.BOW_STRING_1777,Items.CROSSBOW_STRING_9438)
override fun defineListeners() {
onUseWith(IntType.ITEM,STRINGS,*UNSTRUNG_BOWS){ player, string, bow ->
val enum = Fletching.stringMap[bow.id] ?: return@onUseWith false
if (bow.id == Items.UNSTRUNG_COMP_BOW_4825) {
// You shouldn't be able to string a bow
if (getQuestStage(player, Quests.ZOGRE_FLESH_EATERS) < 8) {
player.packetDispatch.sendMessage("You must have started Zogre Flesh Eaters and asked Grish to string this.")
return@onUseWith true
}
}
if(enum.string != string.id){
player.sendMessage("That's not the right kind of string for this.")
return@onUseWith true
}
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(enum.product)) {
override fun create(amount: Int, index: Int) {
player.pulseManager.run(StringPulse(player, string.asItem(), enum, amount))
}
override fun getAll(index: Int): Int {
return player.inventory.getAmount(string.asItem())
}
}
handler.open()
PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 215, 10))
return@onUseWith true
}
onUseWith(IntType.ITEM,ARROW_SHAFT,*FEATHERS){ player, shaft, feather ->
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(FLETCHED_SHAFT)) {
override fun create(amount: Int, index: Int) {
player.pulseManager.run(HeadlessArrowPulse(player, shaft.asItem(), Item(feather.id), amount))
}
override fun getAll(index: Int): Int {
return player.inventory.getAmount(FLETCHED_SHAFT)
}
}
handler.open()
return@onUseWith true
}
onUseWith(IntType.ITEM,OGRE_ARROW_SHAFT,*FEATHERS){ player, shaft, feather ->
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(FLIGHTED_OGRE_ARROW)) {
override fun create(amount: Int, index: Int) {
player.pulseManager.run(HeadlessOgreArrowPulse(player, shaft.asItem(), Item(feather.id, 4), amount))
}
override fun getAll(index: Int): Int {
return player.inventory.getAmount(FLIGHTED_OGRE_ARROW)
}
}
handler.open()
return@onUseWith true
}
onUseWith(IntType.ITEM,FLETCHED_SHAFT,*UNFINISHED_ARROWS){ player, shaft, unfinished ->
val head = Fletching.arrowHeadMap[unfinished.id] ?: return@onUseWith false
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, head.getFinished()) {
override fun create(amount: Int, index: Int) {
player.pulseManager.run(ArrowHeadPulse(player, shaft.asItem(), head, amount))
}
override fun getAll(index: Int): Int {
return player.inventory.getAmount(head.getUnfinished())
}
}
handler.open()
return@onUseWith true
}
onUseWith(IntType.ITEM,MITHRIL_BOLT,MITH_GRAPPLE_TIP){ player, bolt, tip ->
if(player.skills.getLevel(Skills.FLETCHING) < 59){
player.sendMessage("You need a fletching level of 59 to make this.")
return@onUseWith true
}
if(player.inventory.remove(Item(MITHRIL_BOLT,1),tip.asItem())){
player.inventory.add(Item(MITH_GRAPPLE))
}
return@onUseWith true
}
onUseWith(IntType.ITEM,ROPE,MITH_GRAPPLE){ player, rope, grapple ->
if(player.skills.getLevel(Skills.FLETCHING) < 59){
player.sendMessage("You need a fletching level of 59 to make this.")
return@onUseWith true
}
if(player.inventory.remove(rope.asItem(),grapple.asItem())){
player.inventory.add(Item(ROPE_GRAPPLE))
}
return@onUseWith true
}
onUseWith(IntType.ITEM,LIMBIDs,*STOCKIDs){ player, limb, stock ->
val limbEnum = Fletching.limbMap[stock.id] ?: return@onUseWith false
if(limbEnum.limb != limb.id){
player.sendMessage("That's not the right limb to attach to that stock.")
return@onUseWith true
}
val handler: SkillDialogueHandler = object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(limbEnum.product)){
override fun create(amount: Int, index: Int) {
player.pulseManager.run(LimbPulse(player, stock.asItem(), limbEnum, amount))
}
override fun getAll(index: Int): Int {
return player.inventory.getAmount(stock.asItem())
}
}
handler.open()
PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10))
return@onUseWith true
}
/**
* (Long) Kebbit bolts don't need feathers and go 6 at a time so use their own interaction
*/
fun makeKebbitBolt(player : Player, ingredient : Item) : Boolean{
val longBolts = when(ingredient.id){
Items.KEBBIT_SPIKE_10105 -> false
Items.LONG_KEBBIT_SPIKE_10107 -> true
else -> return false
}
val level = if(longBolts) 42 else 26
if (getDynLevel(player, Skills.FLETCHING) < level){
sendMessage(player, "You need a fletching level of $level to create ${if (longBolts) "long " else ""}kebbit bolts.")
return true
}
val finalProduct = if(longBolts) Items.LONG_KEBBIT_BOLTS_10159 else Items.KEBBIT_BOLTS_10158
val xp = if(longBolts) 47.7 else 28.6 // source https://runescape.wiki/w/Fletching?oldid=1069981#Bolts_2
if(removeItem(player, ingredient.id)){
addItem(player, finalProduct, 6)
player.skills.addExperience(Skills.FLETCHING, xp)
animate(player, 885)
}
return true
}
onUseWith(IntType.ITEM, Items.CHISEL_1755, Items.KEBBIT_SPIKE_10105) { player, used, with ->
return@onUseWith makeKebbitBolt(player, with as Item)
}
onUseWith(IntType.ITEM, Items.CHISEL_1755, Items.LONG_KEBBIT_SPIKE_10107) { player, used, with ->
return@onUseWith makeKebbitBolt(player, with as Item)
}
}
}

View file

@ -1,131 +0,0 @@
package content.global.skill.fletching;
import content.global.skill.fletching.items.bolts.BoltPulse;
import content.global.skill.fletching.items.darts.DartPulse;
import org.rs09.consts.Items;
import core.game.dialogue.SkillDialogueHandler;
import core.game.dialogue.SkillDialogueHandler.SkillDialogue;
import core.game.interaction.NodeUsageEvent;
import core.game.interaction.UseWithHandler;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.net.packet.PacketRepository;
import core.net.packet.context.ChildPositionContext;
import core.net.packet.out.RepositionChild;
import core.plugin.Initializable;
import core.plugin.Plugin;
/**
* Represents the plugin used to open the fletching dialogue.
* @author Ceikry
*/
@Initializable
public class FletchingPlugin extends UseWithHandler {
public FletchingPlugin() {
super(819,820,821,822,823,824,11232,9375,9376,9377,9382,9378,9379,9380,9381,13279,1511,1521,1519,1517,1515,1513,2862,6332,6333, Items.MAHOGANY_LOGS_6332);
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
// Knife
addHandler(946, ITEM_TYPE, this);
// Feathers plus colored feathers
addHandler(314, ITEM_TYPE,this);
addHandler(10087, ITEM_TYPE, this);
addHandler(10088, ITEM_TYPE, this);
addHandler(10089, ITEM_TYPE, this);
addHandler(10090, ITEM_TYPE, this);
addHandler(10091, ITEM_TYPE, this);
return this;
}
@Override
public boolean handle(final NodeUsageEvent event) {
final Player player = event.getPlayer();
//handle darts
if(Fletching.isDart(event.getUsedItem().getId())){
final Fletching.Darts dart = Fletching.dartMap.get(event.getUsedItem().getId());
SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, dart.getFinished()) {
@Override
public void create(final int amount, int index) {
player.getPulseManager().run(new DartPulse(player, event.getUsedItem(), dart, amount));
}
@Override
public int getAll(int index) {
return player.getInventory().getAmount(event.getUsedItem());
}
};
handler.open();
return true;
}
//handle bolts
if(Fletching.isBolt(event.getUsedItem().getId()) || Fletching.isBolt(event.getUsedWith().getId())){
// figure out which of the used items is a bolt, and which is potentially a feather
final Fletching.Bolts bolt
= Fletching.isBolt(event.getUsedItem().getId())
? Fletching.boltMap.get(event.getUsedItem().getId())
: Fletching.boltMap.get(event.getUsedWith().getId());
final int featherId
= Fletching.isBolt(event.getUsedItem().getId())
? event.getUsedWith().getId()
: event.getUsedItem().getId();
final boolean hasFeather = (featherId == 314 || (featherId >= 10087 && featherId <= 10091));
if (hasFeather) {
SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, bolt.getFinished()) {
@Override
public void create(final int amount, int index) {
player.getPulseManager().run(new BoltPulse(player, event.getUsedItem(), bolt, new Item(featherId), amount));
}
@Override
public int getAll(int index) {
return player.getInventory().getAmount(event.getUsedItem());
}
};
handler.open();
return true;
}
return false;
}
//handle logs
if(Fletching.isLog(event.getUsedItem().getId()) && event.getUsedWith().getId() == 946) {
final Item log = event.getUsedItem();
Item[] items = Fletching.getItems(log.getId());
SkillDialogue dialLength = SkillDialogue.ONE_OPTION;
switch (items.length) {
case 2:
dialLength = SkillDialogue.TWO_OPTION;
break;
case 3:
dialLength = SkillDialogue.THREE_OPTION;
break;
case 4:
dialLength = SkillDialogue.FOUR_OPTION;
break;
}
SkillDialogueHandler handler = new SkillDialogueHandler(player, dialLength, items) {
@Override
public void create(final int amount, int index) {
final Fletching.FletchingItems item = Fletching.getEntries(log.getId())[index];
player.getPulseManager().run(new FletchingPulse(player, log, amount, item));
}
@Override
public int getAll(int index) {
return player.getInventory().getAmount(log);
}
};
handler.open();
return true;
}
return false;
}
}

View file

@ -1,145 +0,0 @@
package content.global.skill.fletching;
import core.tools.RandomFunction;
import core.game.node.entity.player.link.diary.DiaryType;
import core.game.world.map.zone.ZoneBorders;
import core.game.node.entity.skill.SkillPulse;
import core.game.node.entity.skill.Skills;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.game.world.update.flag.context.Animation;
import core.tools.StringUtils;
import content.data.Quests;
/**
* fletching skill pulse
* @author ceik
*/
public final class FletchingPulse extends SkillPulse<Item> {
/**
* Seers bank zone borders for the diary task
*/
private static final ZoneBorders bankZone = new ZoneBorders(2721,3493,2730,3487);
/**
* Represents the animation used in this generic pulse.
*/
private static final Animation ANIMATION = new Animation(1248);
/**
* Represents the item we are fletching.
*/
private Fletching.FletchingItems fletch;
/**
* Represents the amount to fletch.
*/
private int amount = 0;
/**
* Represents the amount to arrows fletched (for ogre arrow shafts which is a random number from 2-6).
*/
private int finalAmount = 0;
/**
* Constructs a new {@code FletchingPulse.java} {@code Object}.
* @param player
* @param node
*/
public FletchingPulse(final Player player, final Item node, final int amount, final Fletching.FletchingItems fletch) {
super(player, node);
this.amount = amount;
this.fletch = fletch;
}
@Override
public boolean checkRequirements() {
if (player.getSkills().getLevel(Skills.FLETCHING) < fletch.level) {
player.getDialogueInterpreter().sendDialogue("You need a Fletching skill of " + fletch.level + " or above to make " + (StringUtils.isPlusN(fletch.getItem().getName().replace("(u)", "").trim()) ? "an" : "a") + " " + fletch.getItem().getName().replace("(u)", "").trim());
return false;
}
if (amount > player.getInventory().getAmount(node)) {
amount = player.getInventory().getAmount(node);
}
if (fletch == Fletching.FletchingItems.OGRE_ARROW_SHAFT) {
if (player.getQuestRepository().getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING).getStage(player) == 0) {
player.getPacketDispatch().sendMessage("You must have started Big Chompy Bird Hunting to make those.");
return false;
}
}
if (fletch == Fletching.FletchingItems.OGRE_COMP_BOW) {
// Technically, this isn't supposed to show up till you've asked Grish.
if (player.getQuestRepository().getQuest(Quests.ZOGRE_FLESH_EATERS).getStage(player) < 8) {
player.getPacketDispatch().sendMessage("You must have started Zogre Flesh Eaters and asked Grish to make this.");
return false;
}
if (!player.getInventory().contains(2859, 1)) {
player.getPacketDispatch().sendMessage("You need to have Wolf Bones in order to make this.");
return false;
}
}
return true;
}
@Override
public void animate() {
player.animate(ANIMATION);
}
@Override
public boolean reward() {
if(bankZone.insideBorder(player) && fletch == Fletching.FletchingItems.MAGIC_SHORTBOW) {
player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 2);
}
if (getDelay() == 1) {
super.setDelay(4);
return false;
}
if (player.getInventory().remove(node)) {
final Item item = new Item(fletch.id,fletch.amount);
if ( fletch == Fletching.FletchingItems.OGRE_ARROW_SHAFT ) {
// The amount of shafts given is random; between two and six will be made.
finalAmount = RandomFunction.random(2,6);
item.setAmount(finalAmount);
}
if ( fletch == Fletching.FletchingItems.OGRE_COMP_BOW ) {
if (!player.getInventory().contains(2859, 1)) {
return false;
} else {
player.getInventory().remove(new Item(2859));
}
}
player.getInventory().add(item);
player.getSkills().addExperience(Skills.FLETCHING, fletch.experience, true);
String message = getMessage();
player.getPacketDispatch().sendMessage(message);
if (fletch.id == Fletching.FletchingItems.MAGIC_SHORTBOW.id
&& (new ZoneBorders(2721, 3489, 2724, 3493, 0).insideBorder(player)
|| new ZoneBorders(2727, 3487, 2730, 3490, 0).insideBorder(player))
&& !player.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 2)) {
player.setAttribute("/save:diary:seers:fletch-magic-short-bow", true);
}
} else {
return true;
}
amount--;
return amount == 0;
}
/**
* Method used to get the message of the fletch.
* @return the message.
*/
public String getMessage() {
switch (fletch) {
case ARROW_SHAFT:
return "You carefully cut the wood into 15 arrow shafts.";
case OGRE_ARROW_SHAFT:
return "You carefully cut the wood into " + finalAmount + " arrow shafts.";
default:
return "You carefully cut the wood into " + (StringUtils.isPlusN(fletch.getItem().getName()) ? "an" : "a") + " " + fletch.getItem().getName().replace("(u)", "").trim() + ".";
}
}
}

View file

@ -1,69 +0,0 @@
package content.global.skill.fletching
import content.global.skill.fletching.Fletching.GemBolts
import content.global.skill.fletching.items.gem.GemBoltCutPulse
import content.global.skill.fletching.items.gem.GemBoltPulse
import core.api.amountInInventory
import core.game.dialogue.SkillDialogueHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.item.Item
import core.net.packet.PacketRepository
import core.net.packet.context.ChildPositionContext
import core.net.packet.out.RepositionChild
import org.rs09.consts.Items
import kotlin.math.min
class GemBoltListener : InteractionListener {
val gems = intArrayOf(
Items.OYSTER_PEARL_411,
Items.OYSTER_PEARLS_413,
Items.OPAL_1609,
Items.JADE_1611,
Items.RED_TOPAZ_1613,
Items.SAPPHIRE_1607,
Items.EMERALD_1605,
Items.RUBY_1603,
Items.DIAMOND_1601,
Items.DRAGONSTONE_1615,
Items.ONYX_6573
)
val boltBases = GemBolts.values().map { it.base }.toIntArray()
val boltTips = GemBolts.values().map { it.tip }.toIntArray()
override fun defineListeners() {
onUseWith(IntType.ITEM, Items.CHISEL_1755, *gems) { player, used, with ->
val gem = Fletching.gemMap[with.id] ?: return@onUseWith true
object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(gem.gem)) {
override fun create(amount: Int, index: Int) {
player.pulseManager.run(GemBoltCutPulse(player, used as? Item, gem, amount))
}
override fun getAll(index: Int): Int {
return player.inventory.getAmount(gem.gem)
}
}.open()
return@onUseWith true
}
onUseWith(IntType.ITEM, boltBases, *boltTips) {player, used, with ->
val bolt = Fletching.tipMap[with.id] ?: return@onUseWith true
if (used.id != bolt.base || with.id != bolt.tip) return@onUseWith true
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(bolt.product)) {
override fun create(amount: Int, index: Int) {
player.pulseManager.run(GemBoltPulse(player, used as? Item, bolt, amount))
}
override fun getAll(index: Int): Int {
return min(amountInInventory(player, used.id), amountInInventory(player, with.id))
}
}
handler.open()
return@onUseWith true
}
}
}

View file

@ -0,0 +1,21 @@
package content.global.skill.fletching
import core.game.node.Node
import core.game.world.map.zone.ZoneBorders
object Zones {
val seersMagicShortbowAchievementZones = arrayOf(
ZoneBorders(2721, 3489, 2724, 3493, 0),
ZoneBorders(2727, 3487, 2730, 3490, 0),
ZoneBorders(2721, 3493, 2730, 3487)
)
fun inAnyZone(node: Node, zones: Array<ZoneBorders>): Boolean {
for (zone in zones) {
if (zone.insideBorder(node)) {
return true
}
}
return false
}
}

View file

@ -0,0 +1,24 @@
package content.global.skill.fletching.arrow
import org.rs09.consts.Items
enum class ArrowCraftInfo(val tipItemId: Int, val arrowItemId: Int, val level: Int, val experience: Double) {
BRONZE_ARROW(Items.BRONZE_ARROWTIPS_39, Items.BRONZE_ARROW_882, 1, 1.3),
IRON_ARROW(Items.IRON_ARROWTIPS_40, Items.IRON_ARROW_884, 15, 2.5),
STEEL_ARROW(Items.STEEL_ARROWTIPS_41, Items.STEEL_ARROW_886, 30, 5.0),
MITHRIL_ARROW(Items.MITHRIL_ARROWTIPS_42, Items.MITHRIL_ARROW_888, 45, 7.5),
ADAMANT_ARROW(Items.ADAMANT_ARROWTIPS_43, Items.ADAMANT_ARROW_890, 60, 10.0),
RUNE_ARROW(Items.RUNE_ARROWTIPS_44, Items.RUNE_ARROW_892, 75, 12.5),
DRAGON_ARROW(Items.DRAGON_ARROWTIPS_11237,Items.DRAGON_ARROW_11212, 90, 15.0),
BROAD_ARROW(Items.BROAD_ARROW_HEADS_13278, Items.BROAD_ARROW_4160, 52, 15.0);
companion object {
private val arrowCraftInfoByTipId = values().associateBy { it.tipItemId }
val arrowTipIds: IntArray = values().map { arrowCraftInfo: ArrowCraftInfo -> arrowCraftInfo.tipItemId }.toIntArray()
fun fromTipId(tipId: Int) : ArrowCraftInfo? {
return arrowCraftInfoByTipId[tipId]
}
}
}

View file

@ -0,0 +1,112 @@
package content.global.skill.fletching.arrow
import content.global.skill.fletching.Feathers
import core.api.*
import core.game.dialogue.SkillDialogueHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import org.rs09.consts.Items
@Suppress("unused") // Reflectively loaded
class ArrowListeners : InteractionListener {
private val arrowShaft = Items.ARROW_SHAFT_52
val headlessArrow = Items.HEADLESS_ARROW_53
companion object {
const val FLIGHTED_OGRE_ARROW_LEVEL = 5
fun sendLevelCheckFailDialog(player: Player, level: Int) {
sendDialogue(
player,
"You need a fletching level of $level to do this."
)
}
}
override fun defineListeners() {
onUseWith(IntType.ITEM, arrowShaft, *Feathers.all) { player, shaft, feather ->
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(headlessArrow)) {
override fun create(amount: Int, index: Int) {
if (!hasSpaceFor(player, Item(headlessArrow))) {
sendDialogue(player, "You do not have enough inventory space.")
return
}
HeadlessArrowCraftScript(player, feather.id, amount).invoke()
}
override fun getAll(index: Int): Int {
return amountInInventory(player, headlessArrow)
}
}
handler.open()
return@onUseWith true
}
onUseWith(IntType.ITEM, headlessArrow, *ArrowCraftInfo.arrowTipIds) { player, headlessArrow, arrowTip ->
val arrowCraftInfo = ArrowCraftInfo.fromTipId(arrowTip.id) ?: return@onUseWith false
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(arrowCraftInfo.arrowItemId)) {
override fun create(amount: Int, index: Int) {
if (!playerMeetsInitialRequirements()) return
TippedArrowCraftScript(player, arrowCraftInfo, amount).invoke()
}
private fun playerMeetsInitialRequirements(): Boolean {
if (arrowCraftInfo == ArrowCraftInfo.BROAD_ARROW && !getSlayerFlags(player).isBroadsUnlocked()) {
sendDialogue(player, "You need to unlock the ability to create broad arrows.")
return false
}
if (getDynLevel(player, Skills.FLETCHING) < arrowCraftInfo.level) {
sendLevelCheckFailDialog(player, arrowCraftInfo.level)
return false
}
if (!hasSpaceFor(player, Item(arrowCraftInfo.arrowItemId))) {
sendDialogue(player, "You do not have enough inventory space.")
return false
}
return true
}
override fun getAll(index: Int): Int {
return amountInInventory(player, arrowTip.id)
}
}
handler.open()
return@onUseWith true
}
onUseWith(IntType.ITEM, Items.OGRE_ARROW_SHAFT_2864, *Feathers.all) { player, ogreArrowShaft, feather ->
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(Items.FLIGHTED_OGRE_ARROW_2865)) {
override fun create(amount: Int, index: Int) {
if (!playerMeetsInitialRequirements()) return
FlightedOgreArrowCraftScript(player, feather.id, amount).invoke()
}
private fun playerMeetsInitialRequirements(): Boolean {
if (getDynLevel(player, Skills.FLETCHING) < FLIGHTED_OGRE_ARROW_LEVEL) {
sendLevelCheckFailDialog(player, FLIGHTED_OGRE_ARROW_LEVEL)
return false
}
if (!hasSpaceFor(player, Item(Items.FLIGHTED_OGRE_ARROW_2865))) {
sendDialogue(player, "You do not have enough inventory space.")
return false
}
return true
}
override fun getAll(index: Int): Int {
return amountInInventory(player, Items.OGRE_ARROW_SHAFT_2864)
}
}
handler.open()
return@onUseWith true
}
}
}

View file

@ -0,0 +1,88 @@
package content.global.skill.fletching.arrow
import core.api.*
import core.game.interaction.Clocks
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import org.rs09.consts.Items
import kotlin.math.min
/**
* Defines the queueScript for creating a flighted ogre arrow
* @author 'Vexia
* @param player the player.
* @param idOfFeatherUsed the feather to attach to the shaft.
* @param sets the amount of sets to complete.
*/
class FlightedOgreArrowCraftScript(
private val player: Player,
private val idOfFeatherUsed: Int,
private val sets: Int
) {
private val flightedOgreArrow = Items.FLIGHTED_OGRE_ARROW_2865
private val ogreArrowShaft = Items.OGRE_ARROW_SHAFT_2864
private val maximumFlightedOgreArrowsCraftableInOneStage = 6
private val feathersPerArrow = 4
private val initialDelay = 1
private val subsequentDelay = 3
private fun getAmountToCraftForThisStage(shaftsInInventory: Int, feathersInInventory: Int): Int {
val limitOfCraftableArrowsDueToFeathers = feathersInInventory / feathersPerArrow
val totalCraftableArrows = min(shaftsInInventory, limitOfCraftableArrowsDueToFeathers)
return if (totalCraftableArrows > maximumFlightedOgreArrowsCraftableInOneStage) {
maximumFlightedOgreArrowsCraftableInOneStage
} else {
totalCraftableArrows
}
}
fun invoke() {
queueScript(player, initialDelay) { stage ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (getDynLevel(player, Skills.FLETCHING) < ArrowListeners.FLIGHTED_OGRE_ARROW_LEVEL) {
ArrowListeners.sendLevelCheckFailDialog(player, ArrowListeners.FLIGHTED_OGRE_ARROW_LEVEL)
return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...)
}
val featherAmount = amountInInventory(player, idOfFeatherUsed)
val shaftAmount = amountInInventory(player, ogreArrowShaft)
val amountToCraft = getAmountToCraftForThisStage(shaftAmount, featherAmount)
if (
removeItemsIfPlayerHasEnough(
player,
Item(ogreArrowShaft, amountToCraft),
Item(idOfFeatherUsed, amountToCraft * feathersPerArrow)
)
) {
when (amountToCraft) {
1 -> sendMessage(player, "You attach $feathersPerArrow feathers to a shaft.")
else -> {
sendMessage(
player,
"You attach ${amountToCraft * feathersPerArrow} feathers to $amountToCraft arrow shafts."
)
}
}
rewardXP(player, Skills.FLETCHING, amountToCraft.toDouble())
addItem(player, flightedOgreArrow, amountToCraft)
} else {
return@queueScript stopExecuting(player)
}
if (stage >= sets - 1) {
return@queueScript stopExecuting(player)
}
return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true)
}
}
}

View file

@ -0,0 +1,76 @@
package content.global.skill.fletching.arrow
import core.api.*
import core.game.interaction.Clocks
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import org.rs09.consts.Items
import kotlin.math.min
/**
* Defines the queueScript for creating a headless arrow
* @author 'Vexia
* @param player the player.
* @param idOfFeatherUsed the feather to attach to the shaft.
* @param sets the amount of sets to complete.
*/
class HeadlessArrowCraftScript(
private val player: Player,
private val idOfFeatherUsed: Int,
private val sets: Int
) {
private val headlessArrow = Items.HEADLESS_ARROW_53
private val arrowShaft = Items.ARROW_SHAFT_52
private val maximumHeadlessArrowsCraftableInOneStage = 15
private val initialDelay = 1
private val subsequentDelay = 3
private fun getAmountToCraftForThisStage(shaftsInInventory: Int, feathersInInventory: Int): Int {
val smallerItemAmount = min(shaftsInInventory, feathersInInventory)
return if (smallerItemAmount > maximumHeadlessArrowsCraftableInOneStage) {
maximumHeadlessArrowsCraftableInOneStage
} else {
smallerItemAmount
}
}
fun invoke() {
queueScript(player, initialDelay) { stage ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
val featherAmount = amountInInventory(player, idOfFeatherUsed)
val shaftAmount = amountInInventory(player, arrowShaft)
val amountToCraft = getAmountToCraftForThisStage(shaftAmount, featherAmount)
if (
removeItemsIfPlayerHasEnough(
player,
Item(arrowShaft, amountToCraft),
Item(idOfFeatherUsed, amountToCraft)
)
) {
when (amountToCraft) {
1 -> sendMessage(player, "You attach a feather to a shaft.")
else -> {
sendMessage(player, "You attach feathers to $amountToCraft arrow shafts.")
}
}
rewardXP(player, Skills.FLETCHING, amountToCraft.toDouble())
addItem(player, headlessArrow, amountToCraft)
} else {
return@queueScript stopExecuting(player)
}
if (stage >= sets - 1) {
return@queueScript stopExecuting(player)
}
return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true)
}
}
}

View file

@ -0,0 +1,79 @@
package content.global.skill.fletching.arrow
import core.api.*
import core.game.interaction.Clocks
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import org.rs09.consts.Items
import kotlin.math.min
/**
* Represents the arrow head pulse to complete the headless arrow.
* @author 'Vexia
* @param player the player.
* @param arrowCraftInfo provides information about the arrow we're crafting.
* @param sets the amount of sets to complete.
*/
class TippedArrowCraftScript(
private val player: Player,
private val arrowCraftInfo: ArrowCraftInfo,
private val sets: Int
) {
private val headlessArrow = Items.HEADLESS_ARROW_53
private val maximumArrowsCraftableInOneStage = 15
private val initialDelay = 1
private val subsequentDelay = 3
private fun getAmountToCraftForThisStage(headlessArrowsInInventory: Int, tipsInInventory: Int): Int {
val smallerItemAmount = min(headlessArrowsInInventory, tipsInInventory)
return if (smallerItemAmount > maximumArrowsCraftableInOneStage) {
maximumArrowsCraftableInOneStage
} else {
smallerItemAmount
}
}
fun invoke() {
queueScript(player, initialDelay) { stage ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (getDynLevel(player, Skills.FLETCHING) < arrowCraftInfo.level) {
ArrowListeners.sendLevelCheckFailDialog(player, arrowCraftInfo.level)
return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...)
}
val tipsInInventory = amountInInventory(player, arrowCraftInfo.tipItemId)
val headlessArrowsInInventory = amountInInventory(player, headlessArrow)
val amountToCraft = getAmountToCraftForThisStage(headlessArrowsInInventory, tipsInInventory)
if (
removeItemsIfPlayerHasEnough(
player,
Item(headlessArrow, amountToCraft),
Item(arrowCraftInfo.tipItemId, amountToCraft)
)
) {
when (amountToCraft) {
1 -> sendMessage(player, "You attach an arrow head to an arrow shaft.")
else -> {
sendMessage(player, "You attach arrow heads to $amountToCraft arrow shafts.")
}
}
rewardXP(player, Skills.FLETCHING, arrowCraftInfo.experience * amountToCraft)
addItem(player, arrowCraftInfo.arrowItemId, amountToCraft)
} else {
return@queueScript stopExecuting(player)
}
if (stage >= sets - 1) {
return@queueScript stopExecuting(player)
}
return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true)
}
}
}

View file

@ -0,0 +1,39 @@
package content.global.skill.fletching.bolts
import org.rs09.consts.Items
/**
* Provides information pertaining to crafting different bolts
* @property unfinishedBoltItemId the unfinished bolt (unf) Item id used to craft the dart.
* @property finishedItemId the resulting finished bolt Item id.
* @property level the level required to craft.
* @property experience the experience gained by crafting.
*/
enum class BoltCraftInfo(
val unfinishedBoltItemId: Int,
val finishedItemId: Int,
val level: Int,
val experience: Double
) {
BRONZE_BOLT(Items.BRONZE_BOLTS_UNF_9375, Items.BRONZE_BOLTS_877, 9, 0.5),
BLURITE_BOLT(Items.BLURITE_BOLTS_UNF_9376, Items.BLURITE_BOLTS_9139, 24, 1.0),
IRON_BOLT(Items.IRON_BOLTS_UNF_9377, Items.IRON_BOLTS_9140, 39, 1.5),
SILVER_BOLT(Items.SILVER_BOLTS_UNF_9382, Items.SILVER_BOLTS_9145, 43, 2.5),
STEEL_BOLT(Items.STEEL_BOLTS_UNF_9378, Items.STEEL_BOLTS_9141, 46, 3.5),
MITHRIL_BOLT(Items.MITHRIL_BOLTS_UNF_9379, Items.MITHRIL_BOLTS_9142, 54, 5.0),
ADAMANTITE_BOLT(Items.ADAMANT_BOLTSUNF_9380, Items.ADAMANT_BOLTS_9143, 61, 7.0),
RUNITE_BOLT(Items.RUNITE_BOLTS_UNF_9381, Items.RUNE_BOLTS_9144, 69, 10.0),
BROAD_BOLT(Items.UNFINISHED_BROAD_BOLTS_13279, Items.BROAD_TIPPED_BOLTS_13280, 55, 3.0);
companion object {
private val boltCraftInfoByUnfinishedBoltIds = values().associateBy { it.unfinishedBoltItemId }
val unfinishedBoltIds: IntArray = BoltCraftInfo.values()
.map { unfinishedBoltToBoltMapping: BoltCraftInfo -> unfinishedBoltToBoltMapping.unfinishedBoltItemId }
.toIntArray()
fun fromUnfinishedBoltId(unfinishedBoltId: Int): BoltCraftInfo? {
return boltCraftInfoByUnfinishedBoltIds[unfinishedBoltId]
}
}
}

View file

@ -0,0 +1,79 @@
package content.global.skill.fletching.bolts
import core.api.*
import core.game.interaction.Clocks
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import kotlin.math.min
/**
* Represents the bolt pulse class to make bolts.
* @author ceik
* @param player the player.
* @param boltCraftInfo contains information about what bolt we're crafting
* @param feather the feather we're using to craft
* @param sets the amount of sets to craft
*/
class BoltCraftScript(
private val player: Player,
private val boltCraftInfo: BoltCraftInfo,
private val feather: Item,
private val sets: Int
) {
private val maximumBoltsCraftableInOneStage = 10
private val initialDelay = 1
private val subsequentDelay = 3
private fun getAmountToCraftForThisSet(feathersInInventory: Int, unfinishedBoltsInInventory: Int): Int {
val smallerItemAmount = min(feathersInInventory, unfinishedBoltsInInventory)
return if (smallerItemAmount > maximumBoltsCraftableInOneStage) {
maximumBoltsCraftableInOneStage
} else {
smallerItemAmount
}
}
fun invoke() {
queueScript(player, initialDelay) { stage ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (getDynLevel(player, Skills.FLETCHING) < boltCraftInfo.level) {
BoltListeners.sendLevelCheckFailDialog(player, boltCraftInfo.level)
return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...)
}
val featherAmount = amountInInventory(player, feather.id)
val unfinishedBoltAmount = amountInInventory(player, boltCraftInfo.unfinishedBoltItemId)
val amountToCraft = getAmountToCraftForThisSet(featherAmount, unfinishedBoltAmount)
if (
removeItemsIfPlayerHasEnough(
player,
Item(feather.id, amountToCraft),
Item(boltCraftInfo.unfinishedBoltItemId, amountToCraft)
)
) {
when (amountToCraft) {
1 -> sendMessage(player, "You attach a feather to a bolt.")
else -> {
sendMessage(player, "You fletch $amountToCraft bolts")
}
}
rewardXP(player, Skills.FLETCHING, boltCraftInfo.experience * amountToCraft)
addItem(player, boltCraftInfo.finishedItemId, amountToCraft)
} else {
return@queueScript stopExecuting(player)
}
if (stage >= sets - 1) {
return@queueScript stopExecuting(player)
}
return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true)
}
}
}

View file

@ -0,0 +1,65 @@
package content.global.skill.fletching.bolts
import content.global.skill.fletching.Feathers
import core.api.*
import core.game.dialogue.SkillDialogueHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import kotlin.math.min
@Suppress("unused") // Reflectively loaded
class BoltListeners : InteractionListener {
override fun defineListeners() {
onUseWith(IntType.ITEM, Feathers.all, *BoltCraftInfo.unfinishedBoltIds) { player, feather, unfinishedBolt ->
val boltCraftInfo = BoltCraftInfo.fromUnfinishedBoltId(unfinishedBolt.id) ?: return@onUseWith false
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(
player,
SkillDialogue.MAKE_SET_ONE_OPTION,
Item(boltCraftInfo.finishedItemId)
) {
override fun create(amount: Int, index: Int) {
if (!playerMeetsInitialRequirements()) return
BoltCraftScript(player, boltCraftInfo, feather.asItem(), amount).invoke()
}
private fun playerMeetsInitialRequirements(): Boolean {
if (boltCraftInfo == BoltCraftInfo.BROAD_BOLT && !getSlayerFlags(player).isBroadsUnlocked()) {
sendDialogue(player, "You need to unlock the ability to create broad bolts.")
return false
}
if (getDynLevel(player, Skills.FLETCHING) < boltCraftInfo.level) {
sendLevelCheckFailDialog(player, boltCraftInfo.level)
return false
}
if (!hasSpaceFor(player, Item(boltCraftInfo.finishedItemId))) {
sendDialogue(player, "You do not have enough inventory space.")
return false
}
return true
}
override fun getAll(index: Int): Int {
return min(
amountInInventory(player, feather.id),
amountInInventory(player, unfinishedBolt.id)
)
}
}
handler.open()
return@onUseWith true
}
}
companion object {
fun sendLevelCheckFailDialog(player: Player, level: Int) {
sendDialogue(
player,
"You need a fletching level of $level in order to do this."
)
}
}
}

View file

@ -0,0 +1,80 @@
package content.global.skill.fletching.crossbow
import core.api.getDynLevel
import core.api.hasSpaceFor
import core.api.sendDialogue
import core.api.sendMessage
import core.game.dialogue.SkillDialogueHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.net.packet.PacketRepository
import core.net.packet.context.ChildPositionContext
import core.net.packet.out.RepositionChild
import org.rs09.consts.Components
@Suppress("unused") // Reflectively loaded
class LimbingListener : InteractionListener {
override fun defineListeners() {
onUseWith(
IntType.ITEM,
UnfinishedCrossbowCraftInfo.limbIds,
*UnfinishedCrossbowCraftInfo.stockIds
) { player, limb, stock ->
val unfinishedCrossbowCraftInfo = UnfinishedCrossbowCraftInfo.forStockId(stock.id) ?: return@onUseWith false
if (unfinishedCrossbowCraftInfo.limbItemId != limb.id) {
sendMessage(player, "That's not the right limb to attach to that stock.")
return@onUseWith true
}
val handler: SkillDialogueHandler = object : SkillDialogueHandler(
player,
SkillDialogue.ONE_OPTION,
Item(unfinishedCrossbowCraftInfo.unstrungCrossbowItemId)
) {
override fun create(amount: Int, index: Int) {
if (!playerMeetsInitialRequirements()) return
LimbingScript(player, unfinishedCrossbowCraftInfo, amount).invoke()
}
private fun playerMeetsInitialRequirements(): Boolean {
if (getDynLevel(player, Skills.FLETCHING) < unfinishedCrossbowCraftInfo.level) {
sendLevelCheckFailDialog(player, unfinishedCrossbowCraftInfo.level)
return false
}
if (!hasSpaceFor(player, Item(unfinishedCrossbowCraftInfo.unstrungCrossbowItemId))) {
sendDialogue(player, "You do not have enough inventory space.")
return false
}
return true
}
override fun getAll(index: Int): Int {
return player.inventory.getAmount(stock.asItem())
}
}
handler.open()
fixTextOverlappingTheCrossbowIcon(player)
return@onUseWith true
}
}
private fun fixTextOverlappingTheCrossbowIcon(player: Player) {
PacketRepository.send(
RepositionChild::class.java,
ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10)
)
}
companion object {
fun sendLevelCheckFailDialog(player: Player, level: Int) {
sendDialogue(
player,
"You need a fletching level of $level to attach these limbs."
)
}
}
}

View file

@ -0,0 +1,56 @@
package content.global.skill.fletching.crossbow
import core.api.*
import core.game.interaction.Clocks
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
/**
* The queue script for attaching limbs.
* @author Ceikry
* @param player The player
* @param unfinishedCrossbowCraftInfo info about the unfinished (unstrung) crossbow we're crafting
* @param amount to create
*/
class LimbingScript(
private val player: Player,
private val unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo,
private val amount: Int
) {
private val initialDelay = 1
private val subsequentDelay = 6
fun invoke() {
queueScript(player, initialDelay) { stage ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (getDynLevel(player, Skills.FLETCHING) < unfinishedCrossbowCraftInfo.level) {
LimbingListener.sendLevelCheckFailDialog(player, unfinishedCrossbowCraftInfo.level)
return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...)
}
if (
removeItemsIfPlayerHasEnough(
player,
Item(unfinishedCrossbowCraftInfo.stockItemId),
Item(unfinishedCrossbowCraftInfo.limbItemId)
)
) {
addItem(player, unfinishedCrossbowCraftInfo.unstrungCrossbowItemId, 1)
rewardXP(player, Skills.FLETCHING, unfinishedCrossbowCraftInfo.experience)
sendMessage(player, "You attach the metal limbs to the stock.")
animate(player, unfinishedCrossbowCraftInfo.animation)
} else {
return@queueScript stopExecuting(player)
}
if (stage >= amount - 1) {
return@queueScript stopExecuting(player)
}
return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true)
}
}
}

View file

@ -0,0 +1,55 @@
package content.global.skill.fletching.crossbow
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items
/**
* Provides information pertaining to crafting different unfinished (unstrung) crossbows
* @author 'Vexia
* @param stockItemId item id of the stock.
* @param limbItemId item id of the limb.
* @param unstrungCrossbowItemId item id of the product.
* @param level the level.
* @param experience the experience.
* @param animation the animation.
*/
enum class UnfinishedCrossbowCraftInfo
(
val stockItemId: Int,
val limbItemId: Int,
val unstrungCrossbowItemId: Int,
val level: Int,
val experience: Double,
val animation: Animation
) {
WOODEN_STOCK(Items.WOODEN_STOCK_9440, Items.BRONZE_LIMBS_9420, Items.BRONZE_CBOW_U_9454, 9, 12.0, Animation(4436)),
OAK_STOCK(Items.OAK_STOCK_9442, Items.BLURITE_LIMBS_9422, Items.BLURITE_CROSSBOW_9176, 24, 32.0, Animation(4437)),
WILLOW_STOCK(Items.WILLOW_STOCK_9444, Items.IRON_LIMBS_9423, Items.IRON_CBOW_U_9457, 39, 44.0, Animation(4438)),
TEAK_STOCK(Items.TEAK_STOCK_9446, Items.STEEL_LIMBS_9425, Items.STEEL_CBOW_U_9459, 46, 54.0, Animation(4439)),
MAPLE_STOCK(Items.MAPLE_STOCK_9448, Items.MITHRIL_LIMBS_9427, Items.MITHRIL_CBOW_U_9461, 54, 64.0, Animation(4440)),
MAHOGANY_STOCK(
Items.MAHOGANY_STOCK_9450,
Items.ADAMANTITE_LIMBS_9429,
Items.ADAMANT_CBOW_U_9463,
61,
82.0,
Animation(4441)
),
YEW_STOCK(Items.YEW_STOCK_9452, Items.RUNITE_LIMBS_9431, Items.RUNITE_CBOW_U_9465, 69, 100.0, Animation(4442));
companion object {
private val unfinishedCrossbowCraftInfoByStockId = values().associateBy { it.stockItemId }
val limbIds: IntArray =
values().map { unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo -> unfinishedCrossbowCraftInfo.limbItemId }
.toIntArray()
val stockIds: IntArray =
values().map { unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo -> unfinishedCrossbowCraftInfo.stockItemId }
.toIntArray()
fun forStockId(stockId: Int): UnfinishedCrossbowCraftInfo? {
return unfinishedCrossbowCraftInfoByStockId[stockId]
}
}
}

View file

@ -0,0 +1,36 @@
package content.global.skill.fletching.darts
import org.rs09.consts.Items
/**
* Provides information pertaining to crafting different darts
* @author 'Vexia
* @property tipItemId the tip Item id used to craft the dart.
* @property dartItemId the resulting dart Item id.
* @property level the level required to craft.
* @property experience the experience gained by crafting.
*/
enum class DartCraftInfo(
val tipItemId: Int,
val dartItemId: Int,
val level: Int,
val experience: Double) {
BRONZE_DART(Items.BRONZE_DART_TIP_819, Items.BRONZE_DART_806, 1, 1.8),
IRON_DART(Items.IRON_DART_TIP_820, Items.IRON_DART_807, 22, 3.8),
STEEL_DART(Items.STEEL_DART_TIP_821, Items.STEEL_DART_808, 37, 7.5),
MITHRIL_DART(Items.MITHRIL_DART_TIP_822, Items.MITHRIL_DART_809, 52, 11.2),
ADAMANT_DART(Items.ADAMANT_DART_TIP_823, Items.ADAMANT_DART_810, 67, 15.0),
RUNE_DART(Items.RUNE_DART_TIP_824, Items.RUNE_DART_811, 81, 18.8),
DRAGON_DART(Items.DRAGON_DART_TIP_11232, Items.DRAGON_DART_11230, 95, 25.0);
companion object {
private val dartCraftInfoByTipIds = values().associateBy { it.tipItemId }
val tipIDs: IntArray = values().map { dartCraftInfo: DartCraftInfo -> dartCraftInfo.tipItemId }.toIntArray()
fun fromTipID(dartTipId: Int): DartCraftInfo? {
return dartCraftInfoByTipIds[dartTipId]
}
}
}

View file

@ -0,0 +1,78 @@
package content.global.skill.fletching.darts
import content.global.skill.fletching.Feathers
import core.api.*
import core.game.interaction.Clocks
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import kotlin.math.min
/**
* Represents the queueScript to craft a dart.
* @author ceikry
* @param player the player.
* @param dartCraftInfo contains info about the dart we're crafting.
* @param sets count of sets to make
*/
class DartCraftScript(
private val player: Player,
private val dartCraftInfo: DartCraftInfo,
private var sets: Int
) {
private val initialDelay = 1
private val subsequentDelay = 3
private val maximumDartsCraftableInOneStage = 10
private fun getAmountToCraftForThisStage(feathersInInventory: Int, dartTipsInInventory: Int): Int {
val smallerItemAmount = min(feathersInInventory, dartTipsInInventory)
return if (smallerItemAmount > maximumDartsCraftableInOneStage) {
maximumDartsCraftableInOneStage
} else {
smallerItemAmount
}
}
fun invoke() {
queueScript(player, initialDelay) { stage ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (getDynLevel(player, Skills.FLETCHING) < dartCraftInfo.level) {
DartListeners.sendLevelCheckFailDialog(player, dartCraftInfo.level)
return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...)
}
val dartTipsInInventory = amountInInventory(player, dartCraftInfo.tipItemId)
val feathersInInventory = amountInInventory(player, Feathers.STANDARD)
val amountToCraft = getAmountToCraftForThisStage(feathersInInventory, dartTipsInInventory)
if (
removeItemsIfPlayerHasEnough(
player,
Item(Feathers.STANDARD, amountToCraft),
Item(dartCraftInfo.tipItemId, amountToCraft)
)
) {
when (amountToCraft) {
1 -> sendMessage(player, "You attach a feather to a dart.")
else -> {
sendMessage(player, "You attach feathers to $amountToCraft darts.")
}
}
rewardXP(player, Skills.FLETCHING, dartCraftInfo.experience * amountToCraft)
addItem(player, dartCraftInfo.dartItemId, amountToCraft)
} else {
return@queueScript stopExecuting(player)
}
if (stage >= sets - 1) {
return@queueScript stopExecuting(player)
}
return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true)
}
}
}

View file

@ -0,0 +1,59 @@
package content.global.skill.fletching.darts
import content.data.Quests
import content.global.skill.fletching.Feathers
import core.api.*
import core.game.dialogue.SkillDialogueHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import kotlin.math.min
@Suppress("unused") // Reflectively loaded
class DartListeners : InteractionListener {
override fun defineListeners() {
onUseWith(IntType.ITEM, Feathers.STANDARD, *DartCraftInfo.tipIDs) { player, feather, dartTip ->
val dartCraftInfo = DartCraftInfo.fromTipID(dartTip.id) ?: return@onUseWith false
val handler: SkillDialogueHandler =
object :
SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(dartCraftInfo.dartItemId)) {
override fun create(amount: Int, index: Int) {
if (!playerMeetsInitialRequirements()) return
DartCraftScript(player, dartCraftInfo, amount).invoke()
}
private fun playerMeetsInitialRequirements(): Boolean {
if (getDynLevel(player, Skills.FLETCHING) < dartCraftInfo.level) {
sendLevelCheckFailDialog(player, dartCraftInfo.level)
return false
}
if (!isQuestComplete(player, Quests.THE_TOURIST_TRAP)) {
sendDialogue(player, "You need to have completed Tourist Trap to fletch darts.")
return false
}
if (!hasSpaceFor(player, Item(dartCraftInfo.dartItemId))) {
sendDialogue(player, "You do not have enough inventory space.")
return false
}
return true
}
override fun getAll(index: Int): Int {
return min(
amountInInventory(player, feather.id),
amountInInventory(player, dartTip.id)
)
}
}
handler.open()
return@onUseWith true
}
}
companion object {
fun sendLevelCheckFailDialog(player: Player, level: Int) {
sendDialogue(player, "You need a fletching level of $level to do this.")
}
}
}

View file

@ -0,0 +1,77 @@
package content.global.skill.fletching.gem
import core.api.*
import core.game.interaction.Clocks
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import kotlin.math.min
/**
* Represents the attaching of a gem tip to a premade bolt.
* @author Ceikry
* @param player the player.
* @param gemBoltCraftInfo crafting info for adding the gem to the bolt
* @param sets the number of sets to craft.
*/
class AttachGemTipToBoltScript(
private val player: Player,
private val gemBoltCraftInfo: GemBoltsCraftInfo,
private val sets: Int
) {
private val initialDelay = 1
private val subsequentDelay = 3
private val maximumGemBoltsCraftableInOneStage = 10
private fun getAmountToCraftForThisPulse(untippedBoltsInInventory: Int, tipsInInventory: Int): Int {
val smallerItemAmount = min(untippedBoltsInInventory, tipsInInventory)
return if (smallerItemAmount > maximumGemBoltsCraftableInOneStage) {
maximumGemBoltsCraftableInOneStage
} else {
smallerItemAmount
}
}
fun invoke() {
queueScript(player, initialDelay) { stage ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) {
GemBoltListeners.sendGemTipAttachLevelCheckFailDialog(player, gemBoltCraftInfo.level)
return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...)
}
val untippedBoltsInInventory = amountInInventory(player, gemBoltCraftInfo.untippedBoltItemId)
val tipsInInventory = amountInInventory(player, gemBoltCraftInfo.tipItemId)
val amountToCraft = getAmountToCraftForThisPulse(untippedBoltsInInventory, tipsInInventory)
if (
removeItemsIfPlayerHasEnough(
player,
Item(gemBoltCraftInfo.untippedBoltItemId, amountToCraft),
Item(gemBoltCraftInfo.tipItemId, amountToCraft)
)
) {
when (amountToCraft) {
1 -> sendMessage(player, "You attach the tip to the bolt.")
else -> {
sendMessage(player, "You fletch $amountToCraft bolts.")
}
}
rewardXP(player, Skills.FLETCHING, gemBoltCraftInfo.experience * amountToCraft)
addItem(player, gemBoltCraftInfo.tippedBoltItemId, amountToCraft)
} else {
return@queueScript stopExecuting(player)
}
if (stage >= sets - 1) {
return@queueScript stopExecuting(player)
}
return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true)
}
}
}

View file

@ -0,0 +1,79 @@
package content.global.skill.fletching.gem
import core.api.*
import core.game.interaction.Clocks
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
/**
* Represents the queue script for cutting gems into bolt tips
* @author Ceikry
* @param player the player.
* @param gemBoltCraftInfo represents the crafting info for the gem we're cutting.
* @param amount the amount to make.
*/
class CutGemsIntoBoltTipsScript(
private val player: Player,
private val gemBoltCraftInfo: GemBoltsCraftInfo,
private val amount: Int
) {
private val initialDelay = 1
private val craftDelay = 5
private val animationDelay = 6
private var craftingFinished = false
private fun invokeAnimationLoop() {
queueScript(player, 0) {
if (craftingFinished) {
return@queueScript stopExecuting(player)
}
animate(player, gemBoltCraftInfo.gemCutAnimationId)
return@queueScript delayScript(player, animationDelay)
}
}
private fun invokeCraftLoop() {
queueScript(player, 0) { stage ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) {
craftingFinished = true
GemBoltListeners.sendGemTipCutLevelCheckFailDialog(player, gemBoltCraftInfo.level)
return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...)
}
val amountOfTipsToCraft = when (gemBoltCraftInfo) {
GemBoltsCraftInfo.PEARLS -> 24
GemBoltsCraftInfo.PEARL -> 6
GemBoltsCraftInfo.ONYX -> 24
else -> 12
}
if (removeItem(player, Item(gemBoltCraftInfo.gemItemId))) {
addItem(player, gemBoltCraftInfo.tipItemId, amountOfTipsToCraft)
rewardXP(player, Skills.FLETCHING, gemBoltCraftInfo.experience)
} else {
craftingFinished = true
return@queueScript stopExecuting(player)
}
if (stage >= amount - 1) {
craftingFinished = true
return@queueScript stopExecuting(player)
}
return@queueScript delayClock(player, Clocks.SKILLING, craftDelay, true)
}
}
fun invoke() {
queueScript(player, initialDelay) {
invokeAnimationLoop()
invokeCraftLoop()
return@queueScript stopExecuting(player)
}
}
}

View file

@ -0,0 +1,90 @@
package content.global.skill.fletching.gem
import core.api.amountInInventory
import core.api.getDynLevel
import core.api.hasSpaceFor
import core.api.sendDialogue
import core.game.dialogue.SkillDialogueHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import org.rs09.consts.Items
import kotlin.math.min
@Suppress("unused") // Reflectively loaded
class GemBoltListeners : InteractionListener {
override fun defineListeners() {
onUseWith(IntType.ITEM, Items.CHISEL_1755, *GemBoltsCraftInfo.gemIds) { player, _, gem ->
val gemBoltCraftInfo = GemBoltsCraftInfo.forGemId(gem.id) ?: return@onUseWith true
object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(gemBoltCraftInfo.gemItemId)) {
override fun create(amount: Int, index: Int) {
if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) {
sendGemTipCutLevelCheckFailDialog(player, gemBoltCraftInfo.level)
return
}
CutGemsIntoBoltTipsScript(player, gemBoltCraftInfo, amount).invoke()
}
override fun getAll(index: Int): Int {
return amountInInventory(player, gemBoltCraftInfo.gemItemId)
}
}.open()
return@onUseWith true
}
onUseWith(
IntType.ITEM,
GemBoltsCraftInfo.untippedBoltIds,
*GemBoltsCraftInfo.boltTipIds
) { player, untippedBolt, boltTip ->
val bolt = GemBoltsCraftInfo.forTipId(boltTip.id) ?: return@onUseWith false
if (untippedBolt.id != bolt.untippedBoltItemId) return@onUseWith false
val handler: SkillDialogueHandler =
object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(bolt.tippedBoltItemId)) {
override fun create(amount: Int, index: Int) {
if (!playerMeetsInitialRequirements()) return
AttachGemTipToBoltScript(player, bolt, amount).invoke()
}
private fun playerMeetsInitialRequirements(): Boolean {
if (getDynLevel(player, Skills.FLETCHING) < bolt.level) {
sendGemTipAttachLevelCheckFailDialog(player, bolt.level)
return false
}
if (!hasSpaceFor(player, Item(bolt.tippedBoltItemId))) {
sendDialogue(player, "You do not have enough inventory space.")
return false
}
return true
}
override fun getAll(index: Int): Int {
return min(amountInInventory(player, untippedBolt.id), amountInInventory(player, boltTip.id))
}
}
handler.open()
return@onUseWith true
}
}
companion object {
fun sendGemTipCutLevelCheckFailDialog(player: Player, level: Int) {
sendDialogue(
player,
"You need a Fletching level of $level or above to do that."
)
}
fun sendGemTipAttachLevelCheckFailDialog(player: Player, level: Int) {
sendDialogue(
player,
"You need a Fletching level of $level or above to do that."
)
}
}
}

View file

@ -0,0 +1,106 @@
package content.global.skill.fletching.gem
import org.rs09.consts.Items
/**
* Provides information pertaining to crafting different gem tipped bolts
* @property untippedBoltItemId the base untipped bolt item id
* @property gemItemId the gem item id used to craft the bolt tips
* @property gemCutAnimationId the animation to play when the player cuts the gem
* @property tipItemId the item id of the resulting bolt tips
* @property tippedBoltItemId the item id of the finished gem-tipped bolt
* @property level the required level to craft the gem tipped bolt or tips themselves
* @property experience gained creating one set of gem tips, or per bolt tipped
*/
enum class GemBoltsCraftInfo(
var untippedBoltItemId: Int,
var gemItemId: Int,
var gemCutAnimationId: Int,
var tipItemId: Int,
var tippedBoltItemId: Int,
var level: Int,
var experience: Double
) {
OPAL(Items.BRONZE_BOLTS_877, Items.OPAL_1609, 890, Items.OPAL_BOLT_TIPS_45, Items.OPAL_BOLTS_879, 11, 1.6),
JADE(Items.BLURITE_BOLTS_9139, Items.JADE_1611, 891, Items.JADE_BOLT_TIPS_9187, Items.JADE_BOLTS_9335, 26, 2.4),
PEARL(Items.IRON_BOLTS_9140, Items.OYSTER_PEARL_411, 4470, Items.PEARL_BOLT_TIPS_46, Items.PEARL_BOLTS_880, 41, 3.2),
PEARLS(
Items.IRON_BOLTS_9140,
Items.OYSTER_PEARLS_413,
4470,
Items.PEARL_BOLT_TIPS_46,
Items.PEARL_BOLTS_880,
41,
3.2
),
RED_TOPAZ(
Items.STEEL_BOLTS_9141,
Items.RED_TOPAZ_1613,
892,
Items.TOPAZ_BOLT_TIPS_9188,
Items.TOPAZ_BOLTS_9336,
48,
3.9
),
SAPPHIRE(
Items.MITHRIL_BOLTS_9142,
Items.SAPPHIRE_1607,
888,
Items.SAPPHIRE_BOLT_TIPS_9189,
Items.SAPPHIRE_BOLTS_9337,
56,
4.7
),
EMERALD(
Items.MITHRIL_BOLTS_9142,
Items.EMERALD_1605,
889,
Items.EMERALD_BOLT_TIPS_9190,
Items.EMERALD_BOLTS_9338,
58,
5.5
),
RUBY(Items.ADAMANT_BOLTS_9143, Items.RUBY_1603, 887, Items.RUBY_BOLT_TIPS_9191, Items.RUBY_BOLTS_9339, 63, 6.3),
DIAMOND(
Items.ADAMANT_BOLTS_9143,
Items.DIAMOND_1601,
886,
Items.DIAMOND_BOLT_TIPS_9192,
Items.DIAMOND_BOLTS_9340,
65,
7.0
),
DRAGONSTONE(
Items.RUNE_BOLTS_9144,
Items.DRAGONSTONE_1615,
885,
Items.DRAGON_BOLT_TIPS_9193,
Items.DRAGON_BOLTS_9341,
71,
8.2
),
ONYX(Items.RUNE_BOLTS_9144, Items.ONYX_6573, 2717, Items.ONYX_BOLT_TIPS_9194, Items.ONYX_BOLTS_9342, 73, 9.4);
companion object {
private val gemBoltCraftInfoByGemId = values().associateBy { it.gemItemId }
private val gemBoltCraftInfoByBoltTipId = values().associateBy { it.tipItemId }
val untippedBoltIds: IntArray =
values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.untippedBoltItemId }.distinct()
.toIntArray()
val gemIds: IntArray =
values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.gemItemId }.toIntArray()
val boltTipIds: IntArray =
values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.tipItemId }.toIntArray()
fun forGemId(gemId: Int): GemBoltsCraftInfo? {
return gemBoltCraftInfoByGemId[gemId]
}
fun forTipId(tipId: Int): GemBoltsCraftInfo? {
return gemBoltCraftInfoByBoltTipId[tipId]
}
}
}

View file

@ -0,0 +1,67 @@
package content.global.skill.fletching.grapple
import core.api.*
import core.game.interaction.Clocks
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import org.rs09.consts.Items
@Suppress("unused") // Reflectively loaded
class GrappleListeners : InteractionListener {
private val mithrilBolt = Items.MITHRIL_BOLTS_9142
private val mithrilGrappleTip = Items.MITH_GRAPPLE_TIP_9416
private val rope = Items.ROPE_954
private val mithrilGrapple = Items.MITH_GRAPPLE_9418
private val mithrilGrappleWithRope = Items.MITH_GRAPPLE_9419
override fun defineListeners() {
onUseWith(IntType.ITEM, mithrilBolt, mithrilGrappleTip) { player, bolt, tip ->
if (getDynLevel(player, Skills.FLETCHING) < 59) {
sendMessage(player, "You need a fletching level of 59 to make this.")
return@onUseWith true
}
queueScript(player, 0) { _ ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (removeItemsIfPlayerHasEnough(
player,
Item(mithrilBolt, 1),
tip.asItem()
)
) {
addItem(player, mithrilGrapple, 1)
}
delayClock(player, Clocks.SKILLING, 3)
return@queueScript stopExecuting(player)
}
return@onUseWith true
}
onUseWith(IntType.ITEM, rope, mithrilGrapple) { player, rope, grapple ->
if (getDynLevel(player, Skills.FLETCHING) < 59) {
sendMessage(player, "You need a fletching level of 59 to make this.")
return@onUseWith true
}
queueScript(player, 0) { _ ->
if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player)
if (removeItemsIfPlayerHasEnough(
player,
rope.asItem(),
grapple.asItem()
)
) {
addItem(player, mithrilGrappleWithRope, 1)
}
delayClock(player, Clocks.SKILLING, 3)
return@queueScript stopExecuting(player)
}
return@onUseWith true
}
}
}

View file

@ -1,123 +0,0 @@
/*
package core.game.node.entity.skill.fletching.items.arrow;
import org.crandor.game.node.item.Item;
*/
/**
* Represents the enum storing the arrow head information.
* @author 'Vexia
* @note brutal arrows after quest.
*//*
public enum ArrowHead {
BRONZE_ARROW(new Item(39), new Item(882), 1, 2.6),
IRON_ARROW(new Item(40), new Item(884), 15, 3.8),
STEEL_ARROW(new Item(41), new Item(886), 30, 6.3),
MITHRIL_ARROW(new Item(42), new Item(888), 45, 8.8),
ADAMANT_ARROW(new Item(43), new Item(890), 60, 10),
RUNE_ARROW(new Item(44), new Item(892), 75, 13.8),
DRAGON_ARROW(new Item(11237), new Item(11212), 90, 16.3),
BROAD_ARROW(new Item(13278), new Item(4160), 52, 10);
*/
/**
* Constructs a new {@code ArrowHead.java} {@code Object}.
* @param item the item.
* @param product the product.
* @param level the level.
* @param experience the experience.
*//*
ArrowHead(Item item, Item product, int level, double experience) {
this.item = item;
this.product = product;
this.level = level;
this.experience = experience;
}
*/
/**
* Represents the arrow tip.
*//*
private final Item item;
*/
/**
* Represents the product item.
*//*
private final Item product;
*/
/**
* Represents the level required.
*//*
private final int level;
*/
/**
* Represents the experience gained.
*//*
private final double experience;
*/
/**
* Gets the item.
* @return The item.
*//*
public Item getTips() {
return item;
}
*/
/**
* Gets the product.
* @return The product.
*//*
public Item getProduct() {
return product;
}
*/
/**
* Gets the level.
* @return The level.
*//*
public int getLevel() {
return level;
}
*/
/**
* Gets the experience.
* @return The experience.
*//*
public double getExperience() {
return experience;
}
*/
/**
* Gets the arrow head.
* @param item the item.
* @return the arrow head.
*//*
public static ArrowHead forItem(final Item item) {
for (ArrowHead arrow : ArrowHead.values()) {
if (arrow.getTips().getId() == item.getId()) {
return arrow;
}
}
return null;
}
}
*/

View file

@ -1,110 +0,0 @@
package content.global.skill.fletching.items.arrow;
import content.global.skill.slayer.SlayerManager;
import core.game.node.entity.skill.SkillPulse;
import core.game.node.entity.skill.Skills;
import content.global.skill.fletching.Fletching;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import static core.api.ContentAPIKt.*;
/**
* Represents the arrow head pulse to complete the headless arrow.
* @author 'Vexia
*/
public class ArrowHeadPulse extends SkillPulse<Item> {
/**
* Represents the headless arrow item.
*/
private static final Item HEADLESS_ARROW = new Item(53);
/**
* Represents the arrow head.
*/
private final Fletching.ArrowHeads arrow;
/**
* Represents the sets to do.
*/
private int sets;
/**
* Constructs a new {@code ArrowHeadPulse.java} {@code Object}.
* @param player the player.
* @param node the node.
* @param arrow the arrow.
* @param sets the sets.
*/
public ArrowHeadPulse(final Player player, final Item node, final Fletching.ArrowHeads arrow, final int sets) {
super(player, node);
this.arrow = arrow;
this.sets = sets;
}
@Override
public boolean checkRequirements() {
if (arrow.unfinished == 4160) {
if (!SlayerManager.getInstance(player).flags.isBroadsUnlocked()) {
player.getDialogueInterpreter().sendDialogue("You need to unlock the ability to create broad arrows.");
return false;
}
}
if (player.getSkills().getLevel(Skills.FLETCHING) < arrow.level) {
player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + arrow.level + " to do this.");
return false;
}
if (!hasSpaceFor(player, arrow.getFinished())) {
sendDialogue(player, "You do not have enough inventory space.");
return false;
}
return true;
}
@Override
public void animate() {
}
@Override
public boolean reward() {
if (getDelay() == 1) {
super.setDelay(3);
}
Item tip = arrow.getUnfinished();
int tipAmount = player.getInventory().getAmount(arrow.unfinished);
int shaftAmount = player.getInventory().getAmount(HEADLESS_ARROW);
if (tipAmount >= 15 && shaftAmount >= 15) {
HEADLESS_ARROW.setAmount(15);
tip.setAmount(15);
player.getPacketDispatch().sendMessage("You attach arrow heads to 15 arrow shafts.");
} else {
int amount = tipAmount > shaftAmount ? shaftAmount : tipAmount;
HEADLESS_ARROW.setAmount(amount);
tip.setAmount(amount);
player.getPacketDispatch().sendMessage(amount == 1 ? "You attach an arrow head to an arrow shaft." : "You attach arrow heads to " + amount + " arrow shafts.");
}
if (player.getInventory().remove(HEADLESS_ARROW, tip)) {
player.getSkills().addExperience(Skills.FLETCHING, arrow.experience * tip.getAmount(), true);
Item product = arrow.getFinished();
product.setAmount(tip.getAmount());
player.getInventory().add(product);
}
HEADLESS_ARROW.setAmount(1);
tip.setAmount(1);
if (!player.getInventory().containsItem(HEADLESS_ARROW)) {
return true;
}
if (!player.getInventory().containsItem(tip)) {
return true;
}
sets--;
return sets == 0;
}
@Override
public void message(int type) {
}
}

Some files were not shown because too many files have changed in this diff Show more