Compare commits

..

28 commits

Author SHA1 Message Date
Taylor White
4328fb2d48 Merge branch 'roguesDenTest' into 'master'
draft:initial files for rogues den, many things still missing

See merge request 2009scape/2009scape!2017
2025-12-06 11:50:28 +00:00
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
214 changed files with 8512 additions and 8032 deletions

View file

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

View file

@ -207,6 +207,10 @@
"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}-"
},
{
"item_id": "600",
"loc_data": "{1,2438,3187,0,200}-"
},
{
"item_id": "677",
"loc_data": "{1,3369,3378,0,150}-"
@ -671,6 +675,10 @@
"item_id": "11065",
"loc_data": "{1,2928,3289,0,90}-"
},
{
"item_id": "11656",
"loc_data": "{1,2438,3185,0,200}-"
},
{
"item_id": "12494",
"loc_data": "{1,2762,2973,0,60}-"

View file

@ -5575,7 +5575,7 @@
"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.",
"durability": null,
"name": "Lens mould",

View file

@ -6227,6 +6227,22 @@
"range_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.",
"melee_animation": "6199",
@ -6237,13 +6253,13 @@
"magic_animation": "0",
"death_animation": "6190",
"name": "Goblin guard",
"defence_level": "20",
"defence_level": "37",
"safespot": null,
"lifepoints": "28",
"strength_level": "20",
"lifepoints": "43",
"strength_level": "37",
"id": "489",
"range_level": "1",
"attack_level": "20"
"attack_level": "32"
},
{
"examine": "If the mummy is at school",
@ -10518,13 +10534,13 @@
"death_animation": "5329",
"name": "Poison spider",
"safespot": null,
"defence_level": "52",
"lifepoints": "64",
"strength_level": "65",
"defence_level": "28",
"lifepoints": "25",
"strength_level": "28",
"id": "1009",
"aggressive": "true",
"range_level": "1",
"attack_level": "50"
"attack_level": "28"
},
{
"examine": "A green skinned croaker",
@ -36003,7 +36019,7 @@
"name": "Tortoise",
"defence_level": "36",
"safespot": null,
"lifepoints": "51",
"lifepoints": "121",
"strength_level": "36",
"id": "3808",
"range_level": "1",
@ -36077,61 +36093,68 @@
"examine": "A Gnome Arrow-chucker",
"combat_style": "1",
"melee_animation": "190",
"range_animation": "0",
"range_animation": "190",
"respawn_delay": "60",
"defence_animation": "0",
"defence_animation": "193",
"weakness": "2",
"magic_animation": "0",
"death_animation": "196",
"name": "Gnome Archer",
"defence_level": "30",
"defence_level": "1",
"safespot": null,
"lifepoints": "42",
"lifepoints": "10",
"strength_level": "1",
"id": "3814",
"aggressive": "true",
"range_level": "30",
"attack_level": "1"
"range_level": "5",
"projectile": "10",
"attack_level": "1",
"prj_height": "30"
},
{
"examine": "Yee haa!",
"melee_animation": "3969",
"range_animation": "0",
"respawn_delay": "60",
"defence_animation": "0",
"defence_animation": "193",
"weakness": "9",
"magic_animation": "0",
"death_animation": "196",
"name": "Gnome Driver",
"defence_level": "33",
"defence_level": "1",
"safespot": null,
"lifepoints": "47",
"strength_level": "33",
"lifepoints": "10",
"strength_level": "1",
"id": "3815",
"aggressive": "true",
"range_level": "1",
"attack_level": "33"
"attack_level": "5"
},
{
"examine": "A battle mage of the gnomish variety.",
"combat_style": "2",
"start_gfx": "93",
"start_height": "80",
"melee_animation": "3968",
"range_animation": "0",
"magic_level": "34",
"magic_level": "5",
"spell_id": "",
"respawn_delay": "60",
"defence_animation": "0",
"defence_animation": "193",
"weakness": "5",
"magic_animation": "0",
"magic_animation": "200",
"death_animation": "196",
"name": "Gnome Mage",
"defence_level": "34",
"defence_level": "1",
"safespot": null,
"lifepoints": "48",
"lifepoints": "10",
"strength_level": "1",
"id": "3816",
"aggressive": "true",
"range_level": "1",
"attack_level": "1"
"projectile": "94",
"attack_level": "1",
"prj_height": "30"
},
{
"examine": "The cruel tortoise trainer. Boo!",
@ -36160,7 +36183,7 @@
"name": "Tortoise",
"defence_level": "36",
"safespot": null,
"lifepoints": "51",
"lifepoints": "101",
"strength_level": "36",
"id": "3819",
"range_level": "1",
@ -53255,6 +53278,7 @@
"attack_level": "1"
},
{
"examine": "An ugly green creature.",
"name": "Goblin",
"defence_level": "1",
"safespot": null,
@ -73515,10 +73539,6 @@
"name": "Kalron",
"id": "486"
},
{
"name": "Observatory professor",
"id": "488"
},
{
"name": "Hajedy",
"id": "510"
@ -74752,6 +74772,7 @@
},
{
"name": "Solihib",
"movement_radius": "1",
"id": "1433"
},
{
@ -82650,6 +82671,7 @@
"id": "6114"
},
{
"examine": "A man, learned in the ways of the stars.",
"name": "Observatory professor",
"id": "6119"
},

View file

@ -157,7 +157,7 @@
},
{
"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",
@ -193,7 +193,7 @@
},
{
"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",
@ -217,15 +217,15 @@
},
{
"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",
"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",
"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",
@ -325,11 +325,11 @@
},
{
"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",
"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",
@ -345,7 +345,7 @@
},
{
"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",
@ -365,7 +365,7 @@
},
{
"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",
@ -389,7 +389,7 @@
},
{
"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",
@ -489,23 +489,23 @@
},
{
"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",
"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",
"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",
"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",
"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",
@ -513,7 +513,7 @@
},
{
"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",
@ -529,11 +529,11 @@
},
{
"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",
"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",
@ -541,11 +541,11 @@
},
{
"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",
"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",
@ -1357,11 +1357,11 @@
},
{
"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",
"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",
@ -1387,6 +1387,10 @@
"npc_id": "490",
"loc_data": "{1887,5026,0,0,6}-"
},
{
"npc_id": "492",
"loc_data": "{2464,3227,0,1,0}-"
},
{
"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}-"
@ -2433,11 +2437,11 @@
},
{
"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",
"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",
@ -2781,7 +2785,7 @@
},
{
"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",
@ -2829,7 +2833,7 @@
},
{
"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",
@ -3345,11 +3349,11 @@
},
{
"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",
"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",
@ -3771,6 +3775,10 @@
"npc_id": "1427",
"loc_data": "{2957,3025,0,0,0}-"
},
{
"npc_id": "1433",
"loc_data": "{2770,2789,0,1,6}-"
},
{
"npc_id": "1434",
"loc_data": "{2753,2770,0,1,6}-"
@ -4057,7 +4065,7 @@
},
{
"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",
@ -4257,7 +4265,7 @@
},
{
"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",
@ -4541,7 +4549,7 @@
},
{
"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",
@ -4701,19 +4709,19 @@
},
{
"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",
"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",
"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",
"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",
@ -4725,7 +4733,7 @@
},
{
"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",
@ -4737,11 +4745,11 @@
},
{
"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",
"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",
@ -4893,7 +4901,7 @@
},
{
"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",
@ -6313,7 +6321,7 @@
},
{
"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",
@ -7233,7 +7241,7 @@
},
{
"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",
@ -8241,19 +8249,19 @@
},
{
"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",
"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",
"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",
"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",
@ -9005,7 +9013,7 @@
},
{
"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",
@ -10289,11 +10297,11 @@
},
{
"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",
"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",
@ -10419,6 +10427,38 @@
"npc_id": "6117",
"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",
"loc_data": "{2467,3183,0,0,3}-"
@ -12135,10 +12175,6 @@
"npc_id": "7804",
"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",
"loc_data": "{3207,3250,0,0,0}-{3208,3250,0,0,0}-{3209,3250,0,0,0}-"
@ -12301,7 +12337,7 @@
},
{
"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",

View file

@ -20,7 +20,7 @@ import org.rs09.consts.Sounds
import java.util.*
/**
* Represents an enchanted jewellery.
* Represents a piece of enchanted jewellery.
* @author Vexia, downthecrop, Player Name
*/
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(
"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"
)
private val GOBLIN_SCROLL_TEXT = arrayOf(
"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"
)
private val SKELETON_SCROLL_TEXT = arrayOf(
"I'll be waiting at the Champions' Guild to",
"collect your bones.",
"I'll be waiting at the Champions' Guild to collect",
"your bones.",
"",
"Champion of Skeletons"
)
private val ZOMBIE_SCROLL_TEXT = arrayOf(
"You come to Champions' Guild, you fight me,",
"I squish you, I get brains!",
"You come to Champions' Guild, you fight me, I",
"squish you, I get brains!",
"",
"Champion of Zombies"
)
private val GIANT_SCROLL_TEXT = arrayOf(
"Get yourself to the Champions' Guild, if you",
"dare to face me puny human.",
"Get yourself to the Champions' Guild, if you dare",
"to face me puny human.",
"",
"Champion of Giants"
)
@ -93,21 +93,22 @@ class ChampionChallengeListener : InteractionListener, MapArea {
)
private val EARTH_WARRIOR_TEXT = arrayOf(
"I challenge you to a duel, come to the arena beneath",
"the Champion's Guild and fight me if you dare.",
"I challenge you to a duel, come to the arena",
"beneath the Champions' Guild and fight me if you",
"dare.",
"",
"Champion of Earth Warriors"
)
private val JOGRE_SCROLL_TEXT = arrayOf(
"You think you can defeat me? Come to the",
"Champion's Guild and prove it!",
"Champions' Guild and prove it!",
"",
"Champion of Jogres"
)
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!",
"",
"Champion of Lesser Demons"

View file

@ -70,7 +70,7 @@ class ChampionScrollsDropHandler : ChampionScrollsEventHookBase() {
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_STEELWILL_6263,
NPCs.SERGEANT_STRONGSTACK_6261

View file

@ -27,24 +27,24 @@ class LarxusDialogue(val ChallengeStart: Boolean = false) : DialogueFile() {
0 -> {
face(findNPC(NPCs.LARXUS_3050)!!, player!!, 1)
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_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_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_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_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_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_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_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_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_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_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 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 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 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 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 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 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 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 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 any Magic attacks. Do you still want to proceed?").also { stage = 1 }
else -> {
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) {
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 }
4 -> {
@ -65,7 +65,7 @@ class LarxusDialogue(val ChallengeStart: Boolean = false) : DialogueFile() {
3 -> playerl("Nothing thanks.").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.game.node.entity.Entity
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.player.Player
import core.game.node.entity.skill.Skills
@ -63,21 +61,11 @@ class ImpChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,
super.checkImpact(state)
val player = state.attacker
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.estimatedHit = state.maximumHit
}
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
}
sendMessage(player, "Larxus said you couldn't use special attacks in this duel.")
}
}
}

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.Skills
import content.data.skill.SkillingTool
import core.ServerConstants
import core.game.node.item.Item
import core.tools.RandomFunction
import org.rs09.consts.Items
@ -13,6 +14,9 @@ import core.game.world.GameWorld
import core.game.world.repository.Repository
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.
*/
@ -100,7 +104,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
if (ShootingStarPlugin.getStarDust(player) < 200) {
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)
}
@ -130,7 +134,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
override fun message(type: Int) {
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.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.link.TeleportManager.TeleportType
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) {
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.
if (getAttribute(player, "/save:original-loc", null) == null) {
fun kidnapPlayer(npc: NPC, player: Player, dest: Location, playerLine: String? = null, callback: (player: Player, npc: NPC) -> Unit) {
val lockDuration = if (playerLine != null) 4 else 6
lock(player, lockDuration)
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, loc, type)
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)
}
}
}
fun returnPlayer(player: Player) {
player.locks.unlockTeleport()
val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION ?: Location.create(3222, 3218, 0))
teleport(player, destination)
val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION)
teleport(player, destination!!)
unlock(player)
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.poofClear
import core.api.sendMessage
import core.api.setAttribute
import core.api.utils.WeightBasedTable
import core.game.interaction.MovementPulse
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.update.flag.context.Graphics
import core.integrations.discord.Discord
import core.tools.RandomFunction
import core.tools.secondsToTicks
import core.tools.ticksToCycles
import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
import kotlin.math.ceil
import kotlin.math.min
@ -43,6 +46,7 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
event.loot = loot
event.player = player
event.spawnLocation = RegionManager.getSpawnLocation(player, this)
setAttribute(event, "spawned-by-ame", true)
return event
}
@ -101,7 +105,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
}
open fun onTimeUp() {
noteAndTeleport()
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
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
import core.api.getAttribute
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
@ -8,12 +9,12 @@ import org.rs09.consts.NPCs
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.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.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
) {
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 core.api.*
import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
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.update.flag.context.Graphics
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) {
override fun 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.
sendChat("${player.username.capitalize()}! I need a little help with something.")
sendChat("${player.username}! I need a little help with something.")
face(player)
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
kidnapPlayer(this, player, Location(1972, 5002, 0)) { player, _ ->
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.player.link.emote.Emotes
import core.tools.RandomFunction
import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC
import core.api.animate
import core.api.lock
import core.api.utils.WeightBasedTable
class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GILES_2538) {
lateinit var pName: String
lateinit var phrases: Array<String>
override fun tick() {
// Don't speak if we have the interface opened
if (!timerPaused) {
// Over allotted time phrase
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)])
}
sayLine(this, phrases, true, true)
if (ticksLeft == 2) {
lock(player, 2)
}
super.tick()
}
@ -36,15 +26,20 @@ class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NP
override fun init() {
super.init()
pName = player.username.capitalize()
phrases = arrayOf("Greetings $pName, I need your help.",
"ehem... Hello $pName, please talk to me!",
"Hello, are you there $pName?",
"It's really rude to ignore someone, $pName!",
"No-one ignores me!")
phrases = arrayOf(
"Greetings ${player.username}, I need your help.",
"ehem... Hello ${player.username}, please talk to me!",
"Hello, are you there ${player.username}?",
"It's really rude to ignore someone, ${player.username}!",
"No-one ignores me!"
)
player.setAttribute("random:pause", false)
player.setAttribute("certer:reward", false)
sendChat(phrases[0])
animate(this, Emotes.BOW.animation, true)
}
override fun onTimeUp() {
noteAndTeleport()
terminate()
}
}

View file

@ -1,12 +1,9 @@
package content.global.ame.events.drilldemon
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.*
import core.game.interaction.QueueStrength
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.update.flag.context.Animation
import org.rs09.consts.Items
@ -24,13 +21,6 @@ object DrillDemonUtils {
val DD_AREA = ZoneBorders(3158, 4817, 3168, 4823)
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) {
setVarp(player, DD_SIGN_VARP, 0)
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)
}
}
}

View file

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

View file

@ -9,36 +9,28 @@ import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
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!")
private var attackPhrase = false
lateinit var phrases: Array<String>
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() {
super.init()
playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297)
sendChat(this, "'Ello der ${player.username.capitalize()}! *hic*")
phrases = arrayOf(
"'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() {
if (RandomFunction.roll(20) && !attackPhrase)
sendPhrases()
sayLine(this, phrases, true, true)
if (ticksLeft <= 10) {
ticksLeft = 10
if (!attackPhrase)
sendChat("I hates you, ${player.username.capitalize()}!").also { attackPhrase = true }
if (attackDelay <= getWorldTicks())
if (attackDelay <= getWorldTicks()) {
this.attack(player)
}
}
super.tick()
}
@ -47,4 +39,10 @@ class DrunkenDwarfNPC(override var loot: WeightBasedTable? = null) : RandomEvent
this.pulseManager.clear()
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
import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.*
import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC
import core.game.system.timer.impl.AntiMacro
import core.game.world.map.Location
import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
class EvilBobNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.EVIL_BOB_2478) {
override fun init() {
super.init()
sendChat("meow")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendChat(player, "No... what? Nooooooooooooo!")
animate(player, EvilBobUtils.teleAnim)
player.graphics(EvilBobUtils.telegfx)
playAudio(player, Sounds.TELEPORT_ALL_200)
face(player)
kidnapPlayer(this, player, Location(3419, 4776, 0), "No... what? Nooooooooooooo!") { player, _ ->
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
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.*
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.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.game.world.update.flag.context.Animation
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) {
removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen)
removeAll(player, Items.FISHLIKE_THING_6202)

View file

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

View file

@ -1,13 +1,10 @@
package content.global.ame.events.freakyforester
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.*
import org.rs09.consts.Items
import org.rs09.consts.NPCs
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.tools.RandomFunction
@ -28,10 +25,6 @@ object FreakUtils{
player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc)
}
fun teleport(player: Player) {
kidnapPlayer(player, Location.create(2599, 4777 ,0), TeleportManager.TeleportType.INSTANT)
}
fun cleanup(player: Player) {
returnPlayer(player)
removeAttributes(player, freakTask, freakComplete, pheasantKilled)

View file

@ -1,39 +1,21 @@
package content.global.ame.events.freakyforester
import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.*
import org.rs09.consts.NPCs
import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC
import core.game.system.timer.impl.AntiMacro
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Sounds
import core.game.world.map.Location
class FreakyForesterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.FREAKY_FORESTER_2458) {
override fun init() {
super.init()
sendChat("Ah, ${player.username}, just the person I need!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
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)
face(player)
kidnapPlayer(this, player, Location(2599, 4777, 0)) { 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 org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC
import core.api.lock
import core.api.playAudio
import core.api.utils.WeightBasedTable
import org.rs09.consts.Sounds
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() {
if(RandomFunction.random(1,15) == 5){
sendChat(phrases.random().replace("@name",player.username.capitalize()))
sayLine(this, phrases, true, true)
if (ticksLeft == 2) {
lock(player, 2)
}
super.tick()
}
override fun 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)
sendChat(phrases.random().replace("@name",player.username.capitalize()))
}
override fun talkTo(npc: NPC) {
player.dialogueInterpreter.open(GenieDialogue(),npc)
}
override fun onTimeUp() {
noteAndTeleport()
terminate()
}
}

View file

@ -4,33 +4,14 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.*
import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
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.Sounds
class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) {
override fun init() {
super.init()
sendChat("Aha, you'll do ${player.username}!")
face(player)
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
MazeInterface.initMaze(player)
// Note: This event is NOT instanced:
// Sources:
// https://youtu.be/2gpzn9oNdy0 (2007)
@ -39,19 +20,13 @@ class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null
// 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)
kidnapPlayer(this, player, MazeInterface.STARTING_POINTS.random()) { player, _ ->
MazeInterface.initMaze(player)
removeAttribute(player, MazeInterface.MAZE_ATTRIBUTE_CHESTS_OPEN)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
}
}
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 core.api.*
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.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.Sounds
// "::revent [-p] <lt>player name<gt> [-e <lt>event name<gt>]"
class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) {
override fun init() {
super.init()
sendChat("${player.username}, you're under arrest!")
face(player)
player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
kidnapPlayer(this, player, PilloryInterface.LOCATIONS.random()) { player, _ ->
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)
}
setUnclosable(player, player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from."))
}
}
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.utils.WeightBasedTable
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.interaction.QueueStrength
import core.game.node.entity.player.Player
import core.tools.END_DIALOGUE
import org.rs09.consts.Components
import org.rs09.consts.Items
import org.rs09.consts.NPCs
class QuizMasterDialogueFile : DialogueFile() {
class QuizMasterDialogueFile : DialogueLabeller() {
companion object {
const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191
const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct"
@ -74,56 +76,77 @@ class QuizMasterDialogueFile : DialogueFile() {
}
override fun handle(componentID: Int, buttonID: Int) {
when (stage) {
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++ }
1 -> player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...").also { stage++ }
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!!))
player!!.interfaceManager.openChatbox(QUIZMASTER_INTERFACE)
stage++
override fun addConversation() {
assignToIds(NPCs.QUIZ_MASTER_2477)
afterClose { player ->
loadLabel(player, "question")
}
4-> {
if (buttonID == getAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 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>", unclosable = true)
player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...", unclosable = true)
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")
label("question")
manual(unclosable = true) { player, _ ->
setAttribute(player, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player))
val comp = Component(QUIZMASTER_INTERFACE)
player.interfaceManager.openChatbox(comp)
return@manual comp
}
exec { player, _ ->
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) {
npc(FacialExpression.FRIENDLY,"<col=08088A>CONGRATULATIONS!</col>", "You are a <col=8A0808>WINNER</col>!", "Please choose your <col=08088A>PRIZE</col>!")
stage = 5
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 {
npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!")
stage = 3
goto("right")
}
} else {
// Wrong Answer
npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!")
stage = 3
goto("wrong")
}
}
// Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas.
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!!)
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)
}
}
2 -> {
queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int ->
addItemOrDrop(player!!, tableRoll.roll()[0].id)
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)
}
removeAttributes(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER)
stage = END_DIALOGUE
end()
}
}
goto("nowhere")
}
}

View file

@ -4,14 +4,9 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.*
import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
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.Sounds
/**
* Quiz Master NPC:
@ -27,35 +22,14 @@ class QuizMasterNPC(var type: String = "", override var loot: WeightBasedTable?
override fun init() {
super.init()
sendChat("Hey ${player.username}! It's your lucky day!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
kidnapPlayer(player, Location(1952, 4764, 1), TeleportManager.TeleportType.INSTANT)
face(player)
kidnapPlayer(this, player, Location(1952, 4764, 0)) { player, _ ->
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)
}
// Quiz dialogue gets opened automatically on zone entry.
}
}

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.NPCs
import content.global.ame.RandomEventNPC
import core.api.lock
import core.api.utils.WeightBasedTable
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
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() {
if(RandomFunction.random(1,15) == 5){
sendChat(phrases.random().replace("@name",player.username.capitalize()))
sayLine(this, phrases, true, true)
if (ticksLeft == 2) {
lock(player, 2)
}
super.tick()
}
override fun 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()
sendChat(phrases.random().replace("@name",player.username.capitalize()))
}
override fun onTimeUp() {
noteAndTeleport()
terminate()
}
fun assignItem(){
assigned_item = items.random()
player.setAttribute("sandwich-lady:item",assigned_item)
player.setAttribute("sandwich-lady:item", assigned_item)
}
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 org.rs09.consts.NPCs
import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
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.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) {
override fun init() {
super.init()
sendChat("Surprise exam, ${player.username}!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
kidnapPlayer(player, Location(1886, 5025, 0), TeleportManager.TeleportType.INSTANT)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
face(player)
kidnapPlayer(this, player, Location(1886, 5025, 0)) { _, _ ->
/* nothing needed */
}
}

View file

@ -2,7 +2,6 @@ package content.global.ame.events.surpriseexam
import core.game.component.Component
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.map.Location
import org.rs09.consts.Items
import org.rs09.consts.NPCs
@ -66,6 +65,6 @@ class SupriseExamListeners : InteractionListener, MapArea {
}
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
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.*
import core.game.node.entity.impl.PulseType
import core.game.node.entity.player.Player
import core.game.system.task.Pulse
import core.game.world.map.Location
import org.rs09.consts.Components
import org.rs09.consts.Items
import core.game.node.entity.player.link.TeleportManager
object SurpriseExamUtils {
val SE_KEY_INDEX = "supexam:index"

View file

@ -1,12 +1,12 @@
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 content.global.skill.fletching.Fletching
import content.global.skill.fletching.FletchingPulse
import core.game.node.item.Item
import org.rs09.consts.Items
import core.game.bots.SkillingBotAssembler
import core.game.bots.Script
class FletchingBankstander : Script(){
var state = State.FLETCHING
@ -17,7 +17,7 @@ class FletchingBankstander : Script(){
State.FLETCHING -> {
bot.inventory.add(Item(Items.KNIFE_946))
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
}

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

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

View file

@ -1,14 +1,19 @@
package content.global.handlers.item
import core.ServerConstants
import core.api.*
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
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.Sounds
class EnchantJewelleryTabListener : InteractionListener {
private val LVL_1_ENCHANT = mapOf(
Items.SAPPHIRE_RING_1637 to Items.RING_OF_RECOIL_2550,
Items.SAPPHIRE_NECKLACE_1656 to Items.GAMES_NECKLACE8_3853,
@ -21,65 +26,69 @@ class EnchantJewelleryTabListener : InteractionListener {
Items.EMERALD_AMULET_1696 to Items.AMULET_OF_DEFENCE_1729,
Items.EMERALD_BRACELET_11076 to Items.CASTLEWAR_BRACE3_11079
)
private val LVL_3_ENCHANT = mapOf(
Items.RUBY_RING_1641 to Items.RING_OF_FORGING_2568,
Items.RUBY_NECKLACE_1660 to Items.DIGSITE_PENDANT_5_11194,
Items.RUBY_AMULET_1698 to Items.AMULET_OF_STRENGTH_1725,
Items.RUBY_BRACELET_11085 to Items.INOCULATION_BRACE_11088
)
private val LVL_4_ENCHANT = mapOf(
Items.DIAMOND_RING_1643 to Items.RING_OF_LIFE_2570,
Items.DIAMOND_NECKLACE_1662 to Items.PHOENIX_NECKLACE_11090,
Items.DIAMOND_AMULET_1700 to Items.AMULET_OF_POWER_1731,
Items.DIAMOND_BRACELET_11092 to Items.FORINTHRY_BRACE5_11095
)
private val LVL_5_ENCHANT = mapOf(
Items.DRAGONSTONE_RING_1645 to Items.RING_OF_WEALTH4_14646,
Items.DRAGON_NECKLACE_1664 to Items.SKILLS_NECKLACE4_11105,
Items.DRAGONSTONE_AMMY_1702 to Items.AMULET_OF_GLORY4_1712,
Items.DRAGON_BRACELET_11115 to Items.COMBAT_BRACELET4_11118
Items.DRAGONSTONE_RING_1645 to Items.RING_OF_WEALTH_2572,
Items.DRAGON_NECKLACE_1664 to Items.SKILLS_NECKLACE_11113,
Items.DRAGONSTONE_AMMY_1702 to Items.AMULET_OF_GLORY_1704,
Items.DRAGON_BRACELET_11115 to Items.COMBAT_BRACELET_11126
)
private val LVL_6_ENCHANT = mapOf(
Items.ONYX_RING_6575 to Items.RING_OF_STONE_6583,
Items.ONYX_NECKLACE_6577 to Items.BERSERKER_NECKLACE_11128,
Items.ONYX_AMULET_6581 to Items.AMULET_OF_FURY_6585,
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() {
on(IntType.ITEM, "break") {player, node ->
for ((tablet, mapping) in TAB_MAPPING) {
on(tablet, IntType.ITEM, "break") { player, _ ->
sendMessage(player, "Try using the tablet on the item instead.") //TODO authentic message
return@on true
}
for ((unenchanted, enchanted) in mapping) {
onUseWith(IntType.ITEM, tablet, unenchanted) { player, tabItem, node ->
var product = enchanted
if (product == Items.RING_OF_WEALTH_2572 && ServerConstants.RING_OF_WEALTH_TELEPORT) {
product = Items.RING_OF_WEALTH_14638
}
if (removeItem(player, Item(tabItem.id))) {
closeAllInterfaces(player)
delayEntity(player, 1)
queueScript(player, strength = QueueStrength.SOFT) {
val items = when (node.id) {
8016 -> LVL_1_ENCHANT //Sapphire
8017 -> LVL_2_ENCHANT
8018 -> LVL_3_ENCHANT
8019 -> LVL_4_ENCHANT
8020 -> LVL_5_ENCHANT
8021 -> LVL_6_ENCHANT
else -> return@queueScript stopExecuting(player)
}
if (inInventory(player, node.id)) {
for (item in player.inventory.toArray()) {
if (item == null) continue
val product = items[item.id] ?: continue
if (removeItem(player, node.id) && (removeItem(player, item.id)) && addItem(player, product)) {
playAudio(player, Sounds.POH_TABLET_BREAK_979)
animate(player, 4069, true)
break
}
}
val anim = Animation(Animations.POH_TABLET_BREAK_4069)
animate(player, anim, true)
delayEntity(player, anim.duration)
queueScript(player, anim.duration, QueueStrength.SOFT) {
val item = node.asItem()
val ret = replaceSlot(player, item.slot, Item(product), item)
if (ret != item) {
PlayerMonitor.log(player, LogType.DUPE_ALERT, "Unknown slot-replacement problem when enchanting jewellery (adding $product replaced $ret rather than $item)")
}
return@queueScript stopExecuting(player)
}
return@on true
}
return@onUseWith 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
@Initializable
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)
class BarrowsEquipmentRegister : Plugin<Any> {
override fun newInstance(arg: Any?): Plugin<Any> {
EquipmentDegrader.registerSet(TICKS, AHRIM_HOOD)
EquipmentDegrader.registerSet(TICKS, AHRIM_STAFF)
EquipmentDegrader.registerSet(TICKS, AHRIM_TOP)
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)
BarrowsEquipment.getAllEquipmentSets().forEach {
degradationSet ->
EquipmentDegrader.registerSet(BarrowsEquipment.DEGRADATION_TICKS_PER_TIER, degradationSet.toTypedArray())
}
return this
}
override fun fireEvent(identifier: String?, vararg args: Any?): Any {
return Unit
}
}

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,38 +1,33 @@
package content.global.handlers.npc
import content.global.handlers.scenery.BankBoothListener
import core.ServerConstants
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.entity.Entity
import core.game.node.entity.npc.AbstractNPC
import core.game.node.entity.npc.NPC
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.Location
import core.plugin.Initializable
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
* handles all the common interactions like 'bank' and 'collect'.
*
* @author vddCore
* @author Player Name
*/
@Initializable
class BankerNPC : AbstractNPC, InteractionListener {
companion object {
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,
@ -41,22 +36,19 @@ class BankerNPC : AbstractNPC, InteractionListener {
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
)
private val ALL_BANKER_NPC_IDS = intArrayOf(
*SPECIAL_NPC_IDS,
*NPC_IDS
)
companion object {
private const val LUNAR_ISLE_BANK_REGION = 8253
/**
* 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
* 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 {
if (node.location.regionId != LUNAR_ISLE_BANK_REGION)
@ -121,37 +113,35 @@ class BankerNPC : AbstractNPC, InteractionListener {
private fun provideDestinationOverride(entity: Entity, node: Node): Location {
val npc = node as NPC
return when(npc.id) {
/* Ogress bankers are 2x2 with their spawn being offset to south-western tile. */
NPCs.OGRESS_BANKER_7049,
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)
NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050 -> npc.location.transform(3, 1, 0)
/* 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)
/* Special-cased NPCs, idk why */
NPCs.BANKER_6532, NPCs.BANKER_6533, NPCs.BANKER_6534, NPCs.BANKER_6535 -> npc.location.transform(npc.direction, 1)
else -> {
if (npc is BankerNPC) {
npc.findAdjacentBankBoothLocation()?.let {
return it.second
}
}
return npc.location
}
}
}
override fun defineListeners() {
on(ALL_BANKER_NPC_IDS, IntType.NPC, "bank", handler = Companion::attemptBank)
on(ALL_BANKER_NPC_IDS, IntType.NPC, "collect", handler = Companion::attemptCollect)
on(NPC_IDS, IntType.NPC, "bank", handler = Companion::attemptBank)
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() {
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() {
@ -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.
*/
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}.

View file

@ -12,6 +12,7 @@ import core.game.system.timer.impl.AntiMacro
import core.game.worldevents.holiday.HolidayRandomEventNPC
import core.game.worldevents.holiday.HolidayRandomEvents
import core.game.worldevents.holiday.HolidayRandoms
import org.rs09.consts.NPCs
/**
* Handles the NPC talk-to option.
@ -37,11 +38,18 @@ class NPCTalkListener : InteractionListener {
val npc = node.asNpc()
if(RandomEvents.randomIDs.contains(node.id)){
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 {
AntiMacro.getEventNpc(player)?.talkTo(node.asNpc())
}
return@on true
//TODO bring sanity here
}
if (HolidayRandomEvents.holidayRandomIDs.contains(node.id) && node is HolidayRandomEventNPC) {
if(HolidayRandoms.getEventNpc(player) == null || HolidayRandoms.getEventNpc(player) != node.asNpc() || HolidayRandoms.getEventNpc(player)?.finalized == true) {

View file

@ -4,8 +4,7 @@ import core.game.dialogue.DialoguePlugin
import content.data.RepairItem
import core.game.interaction.NodeUsageEvent
import core.game.interaction.UseWithHandler
import content.region.misthalin.lumbridge.dialogue.BobDialogue.BarrowsEquipment
import content.region.misthalin.lumbridge.dialogue.BobDialogue.BarrowsEquipment.BarrowsFullEquipment
import content.global.handlers.item.equipment.BarrowsEquipment
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
@ -14,8 +13,10 @@ import core.plugin.Plugin
import kotlin.math.ceil
import org.rs09.consts.Items
private val ALL_REPAIRABLE_ITEM_IDS = (RepairItem.repairableItemIds + BarrowsEquipment.getAllRepairableBarrowsIds()).toIntArray()
@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> {
addHandler(13715, OBJECT_TYPE, 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 {
event ?: return false
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
if(repairItem != null){
baseCost = repairItem.cost * 1.0
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
}
val repairItem = RepairItem.forId(usedItem.id)
val barrowsDef = BarrowsEquipment.getDefinition(usedItem.id)
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.")
return true
}
val cost: Int = ceil(((100.0 - (player.skills.getLevel(Skills.SMITHING) / 2.0) ) / 100.0) * baseCost).toInt()
player.dialogueInterpreter.open(58824213,event.used,cost,product)
val discountMultiplier = (100.0 - (player.skills.getLevel(Skills.SMITHING) / 2.0)) / 100.0
val cost = ceil(discountMultiplier * baseCost).toInt()
player.dialogueInterpreter.open(58824213,usedItem, cost, product)
return true
}
@Initializable
class RepairDialogue(player: Player? = null) : DialoguePlugin(player){
override fun newInstance(player: Player?): DialoguePlugin {
return RepairDialogue(player)
}
var item: Item? = null
var cost: Int = 0
var product: Item? = null
class RepairDialogue(player: Player? = null) : DialoguePlugin(player) {
private var item: Item? = null
private var cost: Int = 0
private var product: Item? = null
override fun newInstance(player: Player?): DialoguePlugin = RepairDialogue(player)
override fun open(vararg args: Any?): Boolean {
item = args[0] as Item
cost = args[1] as Int
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
return true
}
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
item ?: return false
product ?: return false
when(stage){
0 -> options("Yes, please","No, thanks").also{stage++}
1 -> when(buttonId){
1 -> exchangeItems(item as Item,cost,product as Item).also { end() }
val currentItem = item ?: return false
val currentProduct = product ?: return false
when (stage) {
0 -> {
options("Yes, please", "No, thanks")
stage++
}
1 -> when (buttonId) {
1 -> {
exchangeItems(currentItem, cost, currentProduct)
end()
}
2 -> end()
}
}
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)
if (player.inventory.containsItem(coins) && player.inventory.containsItem(item)) {
player.inventory.remove(item, coins)
player.inventory.add(product)
player.sendMessage("You repair your ${product.name.toLowerCase()} for $cost.")
if (player.inventory.remove(item, coins)) {
if (player.inventory.add(product)) {
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 {
player.sendMessage("You can't afford that.")
}

View file

@ -1,17 +1,22 @@
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.InteractionListener
import core.game.interaction.QueueStrength
import core.game.node.entity.impl.Animator
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.node.scenery.Scenery
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items
import org.rs09.consts.Items.BREAD_DOUGH_2307
import org.rs09.consts.Items.RAW_BEAR_MEAT_2136
import org.rs09.consts.Items.RAW_BEEF_2132
import org.rs09.consts.Items.SEAWEED_401
import org.rs09.consts.Items.UNCOOKED_CAKE_1889
import org.rs09.consts.Sounds
/**
* @author Ceikry
@ -32,8 +37,36 @@ class CookingRewrite : InteractionListener {
}
override fun defineListeners() {
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 obj = with.asScenery()
val range = obj.name.toLowerCase().contains("range")
@ -66,7 +99,7 @@ class CookingRewrite : InteractionListener {
}
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
fun cook(player: Player, `object`: Scenery?, initial: Int, product: Int, amount: Int) {

View file

@ -1,5 +1,6 @@
package content.global.skill.cooking
import content.region.misc.tutisland.handlers.TutorialStage
import core.api.*
import core.game.event.ResourceProducedEvent
import core.game.node.entity.skill.Skills
@ -24,9 +25,18 @@ class DoughMakingListener : InteractionListener {
FULL_WATER_CONTAINERS_TO_EMPTY_CONTAINERS.keys.toIntArray(),
Items.POT_OF_FLOUR_1933
) { player, waterContainer, flourContainer ->
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
}
}
private class DoughMakeDialogue(val waterContainer: Item, val flourContainer: Item) : DialogueFile() {
@ -73,7 +83,7 @@ class DoughMakingListener : InteractionListener {
sendMessage(
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 {

View file

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

View file

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

View file

@ -1,12 +1,15 @@
package content.global.skill.crafting.lightsources
import content.data.LightSource
import core.api.log
import core.api.*
import core.cache.def.impl.ItemDefinition
import core.game.container.Container
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.map.Location
import core.tools.SystemLogger
import core.plugin.Initializable
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}") }
// 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))
return true
}

View file

@ -1,12 +1,16 @@
package content.global.skill.crafting.lightsources
import core.api.*
import core.api.teleport
import core.game.container.Container
import core.game.event.LitLightSourceEvent
import core.game.interaction.NodeUsageEvent
import core.game.interaction.UseWithHandler
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.world.map.Location
import core.plugin.Initializable
import core.plugin.Plugin
@ -72,6 +76,14 @@ class LightSourceLighter : UseWithHandler(590,36,38){
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)){
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
) {
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),
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),

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) {
}
}

View file

@ -1,145 +0,0 @@
package content.global.skill.fletching.items.arrow;
import org.rs09.consts.Items;
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 static core.api.ContentAPIKt.*;
/**
* Represents the arrow pulse for creating unfinished arrows.
* @author 'Vexia
*/
public final class HeadlessArrowPulse extends SkillPulse<Item> {
/**
* Represents the headless arrow item.
*/
private final Item HEADLESS_ARROW = new Item(Items.HEADLESS_ARROW_53);
/**
* Represents the arrow shaft item.
*/
private final Item ARROW_SHAFT = new Item(Items.ARROW_SHAFT_52);
/**
* Represents the feather items.
*/
private static final Item[] FEATHER = new Item[] {
new Item(Items.FEATHER_314),
new Item(Items.STRIPY_FEATHER_10087),
new Item(Items.RED_FEATHER_10088),
new Item(Items.BLUE_FEATHER_10089),
new Item(Items.YELLOW_FEATHER_10090),
new Item(Items.ORANGE_FEATHER_10091)
};
/**
* The feather being used.
*/
private Item feather;
/**
* Represents the amount to make.
*/
private int sets;
/**
* Represents if we should use sets, meaning we have 15 & 15 arrow shafts and feathers.
*/
private boolean useSets = false;
/**
* Constructs a new {@code ArrowPulse.java} {@code Object}.
* @param player the player.
* @param node the node.
*/
public HeadlessArrowPulse(Player player, Item node, Item feather, int sets) {
super(player, node);
this.sets = sets;
this.feather = feather;
}
@Override
public boolean checkRequirements() {
if (!player.getInventory().containsItem(ARROW_SHAFT)) {
player.getDialogueInterpreter().sendDialogue("You don't have any arrow shafts.");
return false;
}
if (feather == null || !player.getInventory().containsItem(feather)) {
player.getDialogueInterpreter().sendDialogue("You don't have any feathers.");
return false;
}
if (player.getInventory().contains(ARROW_SHAFT.getId(), 15) && player.getInventory().contains(feather.getId(), 15)) {
useSets = true;
} else {
useSets = false;
}
if (!hasSpaceFor(player, HEADLESS_ARROW.asItem())) {
sendDialogue(player, "You do not have enough inventory space.");
return false;
}
return true;
}
@Override
public void animate() {
}
@Override
public boolean reward() {
int featherAmount = player.getInventory().getAmount(feather);
int shaftAmount = player.getInventory().getAmount(ARROW_SHAFT);
if (getDelay() == 1) {
super.setDelay(3);
}
if (featherAmount >= 15 && shaftAmount >= 15) {
feather.setAmount(15);
ARROW_SHAFT.setAmount(15);
player.getPacketDispatch().sendMessage("You attach feathers to 15 arrow shafts.");
} else {
int amount = Math.min(featherAmount, shaftAmount);
feather.setAmount(amount);
ARROW_SHAFT.setAmount(amount);
player.getPacketDispatch().sendMessage(amount == 1
? "You attach a feathers to a shaft." : "You attach feathers to " + amount + " arrow shafts.");
}
if (player.getInventory().remove(feather, ARROW_SHAFT)) {
HEADLESS_ARROW.setAmount(feather.getAmount());
player.getSkills().addExperience(Skills.FLETCHING, HEADLESS_ARROW.getAmount(), true);
player.getInventory().add(HEADLESS_ARROW);
}
HEADLESS_ARROW.setAmount(1);
feather.setAmount(1);
ARROW_SHAFT.setAmount(1);
if (!player.getInventory().containsItem(ARROW_SHAFT)) {
return true;
}
if (!player.getInventory().containsItem(feather)) {
return true;
}
sets--;
return sets <= 0;
}
@Override
public void message(int type) {
}
/**
* Gets the feather item.
* @return the item.
*/
private Item getFeather() {
int length = FEATHER.length;
for (int i = 0; i < length; i++) {
Item f = FEATHER[i];
if (player.getInventory().containsItem(f)) {
return f;
}
}
return null;
}
}

View file

@ -1,131 +0,0 @@
package content.global.skill.fletching.items.arrow;
import core.game.node.entity.player.Player;
import core.game.node.entity.skill.SkillPulse;
import core.game.node.entity.skill.Skills;
import core.game.node.item.Item;
import org.rs09.consts.Items;
import static core.api.ContentAPIKt.hasSpaceFor;
import static core.api.ContentAPIKt.sendDialogue;
/**
* Represents the arrow pulse for creating unfinished ogre arrows.
* @author 'Vexia
*/
public final class HeadlessOgreArrowPulse extends SkillPulse<Item> {
/**
* Represents the headless ogre arrow item.
*/
private final Item HEADLESS_ARROW = new Item(Items.FLIGHTED_OGRE_ARROW_2865);
/**
* Represents the ogre arrow shaft item.
*/
private final Item ARROW_SHAFT = new Item(Items.OGRE_ARROW_SHAFT_2864);
/**
* Represents the feather items.
*/
private static final Item[] FEATHER = new Item[] {
new Item(Items.FEATHER_314, 4),
};
/**
* The feather being used.
*/
private Item feather;
/**
* Represents the amount to make.
*/
private int sets;
/**
* Constructs a new {@code ArrowPulse.java} {@code Object}.
* @param player the player.
* @param node the node.
*/
public HeadlessOgreArrowPulse(Player player, Item node, Item feather, int sets) {
super(player, node);
this.sets = sets;
this.feather = feather;
}
@Override
public boolean checkRequirements() {
if (!player.getInventory().containsItem(ARROW_SHAFT)) {
player.getDialogueInterpreter().sendDialogue("You don't have any arrow shafts.");
return false;
}
if (feather == null || !player.getInventory().containsItem(feather)) {
player.getDialogueInterpreter().sendDialogue("You don't have any feathers.");
return false;
}
if (!hasSpaceFor(player, HEADLESS_ARROW.asItem())) {
sendDialogue(player, "You do not have enough inventory space.");
return false;
}
return true;
}
@Override
public void animate() {
}
@Override
public boolean reward() {
int featherAmount = player.getInventory().getAmount(feather);
int shaftAmount = player.getInventory().getAmount(ARROW_SHAFT);
if (getDelay() == 1) {
super.setDelay(3);
}
if (featherAmount >= 24 && shaftAmount >= 6) {
feather.setAmount(24);
ARROW_SHAFT.setAmount(6);
player.getPacketDispatch().sendMessage("You attach 24 feathers to 6 ogre arrow shafts.");
} else {
int amount = Math.min(featherAmount / 4, shaftAmount);
feather.setAmount(amount*4);
ARROW_SHAFT.setAmount(amount);
player.getPacketDispatch().sendMessage(amount == 1
? "You attach a feathers to a shaft." : "You attach " + amount * 4 + " feathers to " + amount + " ogre arrow shafts.");
}
if (player.getInventory().remove(feather, ARROW_SHAFT)) {
HEADLESS_ARROW.setAmount(ARROW_SHAFT.getAmount());
player.getSkills().addExperience(Skills.FLETCHING, HEADLESS_ARROW.getAmount(), true);
player.getInventory().add(HEADLESS_ARROW);
}
HEADLESS_ARROW.setAmount(1);
feather.setAmount(1);
ARROW_SHAFT.setAmount(1);
if (!player.getInventory().containsItem(ARROW_SHAFT)) {
return true;
}
if (!player.getInventory().containsItem(feather)) {
return true;
}
sets--;
return sets <= 0;
}
@Override
public void message(int type) {
}
/**
* Gets the feather item.
* @return the item.
*/
private Item getFeather() {
int length = FEATHER.length;
for (int i = 0; i < length; i++) {
Item f = FEATHER[i];
if (player.getInventory().containsItem(f)) {
return f;
}
}
return null;
}
}

View file

@ -1,123 +0,0 @@
/*
package core.game.node.entity.skill.fletching.items.bolts;
import org.crandor.game.node.item.Item;
*/
/**
* Represents an enum of bolts.
* @author 'Vexia
*//*
public enum Bolt {
BRONZE_BOLT(new Item(9375), new Item(877), 9, 0.5),
BLURITE_BOLT(new Item(9376), new Item(9139), 24, 1),
IRON_BOLT(new Item(9377), new Item(9140), 39, 1.5),
SILVER_BOLT(new Item(9382), new Item(9145), 43, 2.5),
STEEL_BOLT(new Item(9378), new Item(9141), 46, 3.5),
MITHRIL_BOLT(new Item(9379), new Item(9142), 54, 5),
ADAMANTITE_BOLT(new Item(9380), new Item(9143), 61, 7),
RUNITE_BOLT(new Item(9381), new Item(9144), 69, 10),
BROAD_BOLT(new Item(13279), new Item(13280), 55, 3);
*/
/**
* The item required.
*//*
private final Item item;
*/
/**
* The product recieved.
*//*
private final Item product;
*/
/**
* The level required.
*//*
private final int level;
*/
/**
* The experience gained.
*//*
private final double experience;
*/
/**
* Constructs a new {@code Bolt} {@code Object}.
* @param item the item.
* @param product the product.
* @param level the level.
* @param experience the experienece.
*//*
Bolt(Item item, Item product, int level, double experience) {
this.item = item;
this.product = product;
this.level = level;
this.experience = experience;
}
*/
/**
* Gets the item.
* @return The item.
*//*
public Item getItem() {
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;
}
*/
/**
* Method used to get the bolt for the item.
* @param item the item.
* @return the bolt.
*//*
public static Bolt forItem(final Item item) {
for (Bolt bolt : Bolt.values()) {
if (bolt.getItem().getId() == item.getId()) {
return bolt;
}
}
return null;
}
}
*/

View file

@ -1,130 +0,0 @@
package content.global.skill.fletching.items.bolts;
import content.global.skill.slayer.SlayerManager;
import org.rs09.consts.Items;
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;
/**
* Represents the bolt pulse class to make bolts.
* @author ceik
*/
public final class BoltPulse extends SkillPulse<Item> {
/**
* Represents the feather item.
*/
private Item feather;
/**
* Represents possible feather Items
*/
private static final Item[] FEATHER = new Item[] {
new Item(Items.FEATHER_314),
new Item(Items.STRIPY_FEATHER_10087),
new Item(Items.RED_FEATHER_10088),
new Item(Items.BLUE_FEATHER_10089),
new Item(Items.YELLOW_FEATHER_10090),
new Item(Items.ORANGE_FEATHER_10091)
};
/**
* Represents the bolt.
*/
private final Fletching.Bolts bolt;
/**
* Represents the sets to do.
*/
private int sets;
/**
* Represents if we're using sets.
*/
private boolean useSets = false;
/**
* Constructs a new {@code BoltPulse.java} {@code Object}.
* @param player the player.
* @param node the node.
*/
public BoltPulse(Player player, Item node, final Fletching.Bolts bolt, final Item feather, final int sets) {
super(player, node);
this.bolt = bolt;
this.sets = sets;
this.feather = feather;
}
@Override
public boolean checkRequirements() {
if (bolt.getUnfinished().getId() == 13279) {
if (!SlayerManager.getInstance(player).flags.isBroadsUnlocked()) {
player.getDialogueInterpreter().sendDialogue("You need to unlock the ability to create broad bolts.");
return false;
}
}
if (player.getSkills().getLevel(Skills.FLETCHING) < bolt.level) {
player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bolt.level + " in order to do this.");
return false;
}
if (!player.getInventory().containsItem(feather)) {
return false;
}
if (!player.getInventory().containsItem(bolt.getUnfinished())) {
return false;
}
if (!player.getInventory().hasSpaceFor(bolt.getFinished())) {
player.getDialogueInterpreter().sendDialogue("You do not have enough inventory space.");
return false;
}
return true;
}
@Override
public void animate() {
}
@Override
public boolean reward() {
int featherAmount = player.getInventory().getAmount(feather);
int boltAmount = player.getInventory().getAmount(bolt.unfinished);
if (getDelay() == 1) {
super.setDelay(3);
}
final Item unfinished = bolt.getUnfinished();
if (featherAmount >= 10 && boltAmount >= 10) {
feather.setAmount(10);
unfinished.setAmount(10);
player.getPacketDispatch().sendMessage("You fletch 10 bolts.");
} else {
int amount = featherAmount > boltAmount ? boltAmount : featherAmount;
feather.setAmount(amount);
unfinished.setAmount(amount);
player.getPacketDispatch().sendMessage(amount == 1 ? "You attach a feather to a bolt." : "You fletch " + amount + " bolts");
}
if (player.getInventory().remove(feather, unfinished)) {
Item product = bolt.getFinished();
product.setAmount(feather.getAmount());
player.getSkills().addExperience(Skills.FLETCHING, product.getAmount() * bolt.experience, true);
player.getInventory().add(product);
}
feather.setAmount(1);
if (!player.getInventory().containsItem(feather)) {
return true;
}
if (!player.getInventory().containsItem(bolt.getUnfinished())) {
return true;
}
sets--;
return sets <= 0;
}
@Override
public void message(int type) {
}
}

View file

@ -1,136 +0,0 @@
/*
package core.game.node.entity.skill.fletching.items.bow;
import org.crandor.game.node.item.Item;
import org.crandor.game.world.update.flag.context.Animation;
*/
/**
* Represents the enum of stringing bows.
* @author 'Vexia
*//*
public enum StringBow {
SHORT_BOW(new Item(50), new Item(841), 5, 5, new Animation(6678)),
LONG_BOW(new Item(48), new Item(839), 10, 10, new Animation(6684)),
OAK_SHORTBOW(new Item(54), new Item(843), 20, 16.5, new Animation(6679)),
OAK_LONGBOW(new Item(56), new Item(845), 25, 25, new Animation(6685)),
WILLOW_SHORTBOW(new Item(60), new Item(849), 35, 33.3, new Animation(6680)),
WILLOW_LONGBOW(new Item(58), new Item(847), 40, 41.5, new Animation(6686)),
MAPLE_SHORTBOW(new Item(64), new Item(853), 50, 50, new Animation(6681)),
MAPLE_LONGBOW(new Item(62), new Item(851), 55, 58.3, new Animation(6687)),
YEW_SHORTBOW(new Item(68), new Item(857), 65, 66, new Animation(6682)),
YEW_LONGBOW(new Item(66), new Item(855), 70, 75, new Animation(6688)),
MAGIC_SHORTBOW(new Item(72), new Item(861), 80, 83.3, new Animation(6683)),
MAGIC_LONGBOW(new Item(70), new Item(859), 85, 91.5, new Animation(6689));
StringBow(final Item item, final Item product, final int level, final double experience, final Animation animation) {
this.item = item;
this.product = product;
this.level = level;
this.experience = experience;
this.animation = animation;
}
*/
/**
* The item required.
*//*
private final Item item;
*/
/**
* The item product.
*//*
private final Item product;
*/
/**
* The level required.
*//*
private final int level;
*/
/**
* The experience required.
*//*
private final double experience;
*/
/**
* The animation of stringing.
*//*
private final Animation animation;
*/
/**
* Gets the item.
* @return The item.
*//*
public Item getItem() {
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;
}
*/
/**
* Method used to get the animation.
* @return the animation.
*//*
public Animation getAnimation() {
return animation;
}
*/
/**
* Method used to get the string bow for the item.
* @param item the item.
* @return the string bow.
*//*
public static StringBow forItem(final int id) {
for (StringBow bw : StringBow.values()) {
if (bw.getItem().getId() == id) {
return bw;
}
}
return null;
}
}*/

View file

@ -1,103 +0,0 @@
package content.global.skill.fletching.items.bow;
import core.game.node.entity.player.info.LogType;
import core.game.node.entity.player.info.PlayerMonitor;
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 content.global.skill.fletching.Fletching;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import static core.api.ContentAPIKt.amountInInventory;
/**
* Represents the skill pulse of stringing.
*
* @author Ceikry
*/
public class StringPulse extends SkillPulse<Item> {
/**
* Represents the string bow.
*/
private final Fletching.String bow;
/**
* The amount.
*/
private int amount;
private int initialAmount;
private int processedAmount;
/**
* Constructs a new {@code StringbowPlugin.java} {@code Object}.
*
* @param player the player.
* @param node the node.
*/
public StringPulse(Player player, Item node, final Fletching.String bow, int amount) {
super(player, node);
this.bow = bow;
this.amount = amount;
this.initialAmount = amountInInventory(player, node.getId());
this.processedAmount = 0;
}
@Override
public boolean checkRequirements() {
if (getDelay() == 1) {
setDelay(2);
}
if (player.getSkills().getLevel(Skills.FLETCHING) < bow.level) {
player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bow.level + " to string this bow.");
return false;
}
if (!player.getInventory().containsItem(new Item(bow.unfinished))) {
return false;
}
if (!player.getInventory().containsItem(new Item(bow.string))) {
player.getDialogueInterpreter().sendDialogue("You seem to have run out of bow strings.");
return false;
}
animate();
return true;
}
@Override
public void animate() {
player.animate(bow.animation);
}
@Override
public boolean reward() {
if (player.getInventory().remove(new Item(bow.unfinished), new Item(bow.string))) {
player.getInventory().add(new Item(bow.product));
player.getSkills().addExperience(Skills.FLETCHING, bow.experience, true);
player.getPacketDispatch().sendMessage("You add a string to the bow.");
processedAmount++;
if (processedAmount > initialAmount) {
PlayerMonitor.log(player, LogType.DUPE_ALERT, "fletched item (" + player.getName() + ", " + bow.unfinished + "): initialAmount " + initialAmount + ", processedAmount " + processedAmount);
}
if (bow == Fletching.String.MAGIC_SHORTBOW
&& (new ZoneBorders(2721, 3489, 2724, 3493, 0).insideBorder(player)
|| new ZoneBorders(2727, 3487, 2730, 3490, 0).insideBorder(player))
&& player.getAttribute("diary:seers:fletch-magic-short-bow", false)) {
player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 2);
}
}
if (!player.getInventory().containsItem(new Item(bow.string)) || !player.getInventory().containsItem(new Item(bow.unfinished))) {
return true;
}
amount--;
return amount == 0;
}
@Override
public void message(int type) {
}
}

View file

@ -1,97 +0,0 @@
/*
package core.game.node.entity.skill.fletching.items.crossbow;
import core.game.node.entity.skill.SkillPulse;
import core.game.node.entity.skill.Skills;
import org.crandor.game.node.entity.player.Player;
import org.crandor.game.node.item.Item;
*/
/**
* Represents the skill pulse of stringing.
* @author 'Vexia
*//*
public class CrossbowPulse extends SkillPulse<Item> {
*/
/**
* Represents the bow string item.
*//*
private final Item BOW_STRING = new Item(9438);
*/
/**
* Represents the string bow.
*//*
private final StringCross bow;
*/
/**
* Represents the amount.
*//*
private int amount;
*/
/**
* Constructs a new {@code StringcrossbowPlugin.java} {@code Object}.
* @param player the player.
* @param node the node.
*//*
public CrossbowPulse(Player player, Item node, final StringCross bow, int amount) {
super(player, node);
this.bow = bow;
this.amount = amount;
}
@Override
public boolean checkRequirements() {
if (player.getSkills().getLevel(Skills.FLETCHING) < bow.getLevel()) {
player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bow.getLevel() + " to string this crossbow.");
return false;
}
if (!player.getInventory().containsItem(BOW_STRING)) {
player.getDialogueInterpreter().sendDialogue("You seem to have run out of bow strings.");
return false;
}
return true;
}
@Override
public void animate() {
player.animate(bow.getAnimation());
}
@Override
public boolean reward() {
if (getDelay() == 1) {
super.setDelay(5);
return false;
}
if (player.getInventory().remove(bow.getItem(), BOW_STRING)) {
player.getInventory().add(bow.getProduct());
player.getSkills().addExperience(Skills.FLETCHING, bow.getExperience(), true);
player.getPacketDispatch().sendMessage("You add a string to the crossbow.");
}
if (!player.getInventory().containsItem(BOW_STRING) || !player.getInventory().containsItem(bow.getItem())) {
return true;
}
amount--;
return amount == 0;
}
@Override
public void message(int type) {
switch (type) {
case 0:
break;
case 1:
break;
}
}
}*/

View file

@ -1,128 +0,0 @@
package content.global.skill.fletching.items.crossbow;
import core.game.node.item.Item;
import core.game.world.update.flag.context.Animation;
/**
* Represents the enum for limbs.
* @author 'Vexia
*/
public enum Limb {
WOODEN_STOCK(new Item(9440), new Item(9420), new Item(9454), 9, 12, new Animation(4436)),
OAK_STOCK(new Item(9442), new Item(9422), new Item(9176), 24, 32, new Animation(4437)),
WILLOW_STOCK(new Item(9444), new Item(9423), new Item(9457), 39, 44, new Animation(4438)),
TEAK_STOCK(new Item(9446), new Item(9425), new Item(9459), 46, 54, new Animation(4439)),
MAPLE_STOCK(new Item(9448), new Item(9427), new Item(9461), 54, 64, new Animation(4440)),
MAHOGANY_STOCK(new Item(9450), new Item(9429), new Item(9463), 61, 82, new Animation(4441)),
YEW_STOCK(new Item(9452), new Item(9431), new Item(9465), 69, 100, new Animation(4442));
/**
* Constructs a new {@code StringcrosbowPlugin.java} {@code Object}.
* @param stock the stock.
* @param limb the limb.
* @param product the product.
* @param level the level.
* @param experience the experience.
* @param animation the animation.
*/
Limb(Item stock, Item limb, Item 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;
}
/**
* The stock.
*/
private final Item stock;
/**
* The limb.
*/
private final Item limb;
/**
* The product.
*/
private final Item product;
/**
* The level.
*/
private final int level;
/**
* The experience.
*/
private final double experience;
/**
* The animation.
*/
private final Animation animation;
/**
* Gets the stock.
* @return The stock.
*/
public Item getStock() {
return stock;
}
/**
* Gets the limb.
* @return The limb.
*/
public Item getLimb() {
return limb;
}
/**
* 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 animation.
* @return The animation.
*/
public Animation getAnimation() {
return animation;
}
/**
* Method used to get the {@link Limb} for the item.
* @param item the item.
* @return the limb.
*/
public static Limb forItems(final Item item, final Item second) {
for (Limb l : Limb.values()) {
if (l.getLimb().getId() == item.getId() && l.getStock().getId() == second.getId() || l.getLimb().getId() == second.getId() && l.getStock().getId() == item.getId()) {
return l;
}
}
return null;
}
}

View file

@ -1,52 +0,0 @@
package content.global.skill.fletching.items.crossbow
import core.game.node.entity.player.Player
import core.game.node.entity.skill.SkillPulse
import core.game.node.entity.skill.Skills
import content.global.skill.fletching.Fletching
import core.game.node.item.Item
/**
* Represents the skill pulse of attaching limbs.
* @author Ceikry
*/
class LimbPulse(player: Player?, node: Item, private val limb: Fletching.Limb, private var amount: Int) : SkillPulse<Item?>(player, node) {
override fun checkRequirements(): Boolean {
if (player.skills.getLevel(Skills.FLETCHING) < limb.level) {
player.dialogueInterpreter.sendDialogue("You need a fletching level of " + limb.level + " to attach these limbs.")
return false
}
if (!player.inventory.containsItem(Item(limb.limb))) {
player.dialogueInterpreter.sendDialogue("That's not the correct limb to attach.")
return false
}
if(!player.inventory.containsItem(Item(limb.stock))){
player.dialogueInterpreter.sendDialogue("That's not the correct stock for that limb.")
return false
}
return player.inventory.containsItem(Item(limb.stock))
}
override fun animate() {
player.animate(limb.animation)
}
override fun reward(): Boolean {
if (delay == 1) {
super.setDelay(6)
return false
}
if (player.inventory.remove(Item(limb.stock), Item(limb.limb))) {
player.inventory.add(Item(limb.product))
player.skills.addExperience(Skills.FLETCHING, limb.experience, true)
player.packetDispatch.sendMessage("You attach the metal limbs to the stock.")
}
if (!player.inventory.containsItem(Item(limb.limb))) {
return true
}
amount--
return amount == 0
}
override fun message(type: Int) {}
}

View file

@ -1,139 +0,0 @@
/*
package core.game.node.entity.skill.fletching.items.crossbow;
import org.crandor.game.node.item.Item;
import org.crandor.game.world.update.flag.context.Animation;
*/
/**
* Represents the enum of stringing crossbows.
* @author 'Vexia
*//*
public enum StringCross {
BRONZE_CBOW(new Item(9454), new Item(9174), 9, 6, new Animation(6671)),
BLURITE_CBOW(new Item(9456), new Item(9176), 24, 16, new Animation(6672)),
IRON_CBOW(new Item(9457), new Item(9177), 39, 22, new Animation(6673)),
STEEL_CBOW(new Item(9459), new Item(9179), 46, 27, new Animation(6674)),
MITHIRIL_CBOW(new Item(9461), new Item(9181), 54, 32, new Animation(6675)),
ADAMANT_CBOW(new Item(9463), new Item(9183), 61, 41, new Animation(6676)),
RUNITE_CBOW(new Item(9465), new Item(9185), 69, 50, new Animation(6677));
*/
/**
* Constructs a new {@code StringcrossbowPlugin.java} {@code Object}.
* @param item the item.
* @param product the product.
* @param level the level.
* @param experience the experience.
*//*
StringCross(final Item item, final Item product, final int level, final double experience, final Animation animation) {
this.item = item;
this.product = product;
this.level = level;
this.experience = experience;
this.animation = animation;
}
*/
/**
* The item required.
*//*
private final Item item;
*/
/**
* The item product.
*//*
private final Item product;
*/
/**
* The level required.
*//*
private final int level;
*/
/**
* The experience required.
*//*
private final double experience;
*/
/**
* The animation of stringing.
*//*
private final Animation animation;
*/
/**
* Gets the item.
* @return The item.
*//*
public Item getItem() {
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;
}
*/
/**
* Method used to get the animation.
* @return the animation.
*//*
public Animation getAnimation() {
return animation;
}
*/
/**
* Method used to get the string bow for the item.
* @param item the item.
* @return the string bow.
*//*
public static StringCross forItem(final Item item) {
for (StringCross bw : StringCross.values()) {
if (bw.getItem().getId() == item.getId()) {
return bw;
}
}
return null;
}
}
*/

View file

@ -1,96 +0,0 @@
package content.global.skill.fletching.items.darts;
import core.game.node.item.Item;
/**
* Represents the enum to hold dart info.
* @author 'Vexia
*/
public enum Dart {
BRONZE_DART(new Item(819), new Item(806), 1, 1.8),
IRON_DART(new Item(820), new Item(807), 22, 3.8),
STEEL_DART(new Item(821), new Item(808), 37, 7.5),
MITHRIL_DART(new Item(822), new Item(809), 52, 11.2),
ADAMANT_DART(new Item(823), new Item(810), 67, 15),
RUNE_DART(new Item(824), new Item(811), 81, 18.8),
DRAGON_DART(new Item(11232), new Item(11230), 95, 25);
/**
* Constructs a new {@code Dart} {@code Object}.
* @param item the item.
* @param product the product.
* @param level the level.
* @param experience the experience.
*/
Dart(final Item item, final Item product, final int level, final double experience) {
this.item = item;
this.product = product;
this.level = level;
this.experience = experience;
}
/**
* Represents the item required.
*/
private final Item item;
/**
* Represents the product gained.
*/
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 getItem() {
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;
}
/**
* Method used to get the dart for the item.
* @param item the item.
* @return the dart.
*/
public static Dart forItem(final Item item) {
for (Dart dart : Dart.values()) {
if (dart.getItem().getId() == item.getId()) {
return dart;
}
}
return null;
}
}

View file

@ -1,105 +0,0 @@
package content.global.skill.fletching.items.darts;
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.*;
import content.data.Quests;
/**
* Represents the dart pulse.
* @author ceikry
*/
public final class DartPulse extends SkillPulse<Item> {
/**
* Represents the feather item.
*/
private static final Item FEATHER = new Item(314);
/**
* Represents the dart.
*/
private final Fletching.Darts dart;
/**
* Represents the sets to make.
*/
private int sets;
/**
* Constructs a new {@code DartPulse.java} {@code Object}.
* @param player the player.
* @param node the node.
*/
public DartPulse(Player player, Item node, Fletching.Darts dart, int sets) {
super(player, node);
this.dart = dart;
this.sets = sets;
}
@Override
public boolean checkRequirements() {
if (player.getSkills().getLevel(Skills.FLETCHING) < dart.level) {
player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + dart.level + " to do this.");
return false;
}
if (!player.getQuestRepository().isComplete(Quests.THE_TOURIST_TRAP)){
player.getDialogueInterpreter().sendDialogue("You need to have completed Tourist Trap to fletch darts.");
return false;
}
if (!hasSpaceFor(player, dart.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);
}
final Item unfinished = dart.getUnfinished();
final int dartAmount = player.getInventory().getAmount(unfinished);
final int featherAmount = player.getInventory().getAmount(FEATHER);
if (dartAmount >= 10 && featherAmount >= 10) {
FEATHER.setAmount(10);
unfinished.setAmount(10);
player.getPacketDispatch().sendMessage("You attach feathers to 10 darts.");
} else {
int amount = featherAmount > dartAmount ? dartAmount : featherAmount;
FEATHER.setAmount(amount);
unfinished.setAmount(amount);
player.getPacketDispatch().sendMessage(amount == 1 ? "You attach a feather to a dart." : "You attach feathers to " + amount + " darts.");
}
if (player.getInventory().remove(FEATHER, unfinished)) {
Item product = dart.getFinished();
product.setAmount(FEATHER.getAmount());
player.getSkills().addExperience(Skills.FLETCHING, dart.experience * product.getAmount(), true);
player.getInventory().add(product);
}
FEATHER.setAmount(1);
if (!player.getInventory().containsItem(FEATHER)) {
return true;
}
if (!player.getInventory().containsItem(dart.getUnfinished())) {
return true;
}
sets--;
return sets == 0;
}
@Override
public void message(int type) {
}
}

View file

@ -1,101 +0,0 @@
package content.global.skill.fletching.items.gem;
import core.game.node.item.Item;
/**
* Represents gems to cut into bolt tips.
* @author 'Vexia
* @date 01/12/2013
*/
public enum Gem {
OPAL(new Item(1609), new Item(45, 12), 11, 1.5),
JADE(new Item(1611), new Item(9187, 12), 26, 2.4),
RED_TOPAZ(new Item(1613), new Item(9188, 12), 48, 3.9),
SAPPHIRE(new Item(1607), new Item(9189, 12), 56, 4),
EMERALD(new Item(1605), new Item(9190, 12), 58, 5.5),
RUBY(new Item(1603), new Item(9191, 12), 63, 6.3),
DIAMOND(new Item(1601), new Item(9192, 12), 65, 7),
DRAGONSTONE(new Item(1615), new Item(9193, 12), 71, 8.2),
ONYX(new Item(6573), new Item(9194, 24), 73, 9.4);
/**
* Constructs a new {@code Gem.java} {@code Object}.
* @param gem the gem.
* @param bolt the bolt.
* @param level the level.
* @param experience the experience.
*/
Gem(Item gem, Item bolt, int level, double experience) {
this.gem = gem;
this.bolt = bolt;
this.level = level;
this.experience = experience;
}
/**
* Represents the gem.
*/
private final Item gem;
/**
* Represents the bolt.
*/
private final Item bolt;
/**
* Represents the level required.
*/
private final int level;
/**
* Represents the experience gained.
*/
private final double experience;
/**
* Gets the gem.
* @return The gem.
*/
public Item getGem() {
return gem;
}
/**
* Gets the bolt.
* @return The bolt.
*/
public Item getBolt() {
return bolt;
}
/**
* Gets the level.
* @return The level.
*/
public int getLevel() {
return level;
}
/**
* Gets the experience.
* @return The experience.
*/
public double getExperience() {
return experience;
}
/**
* Method used to get a gem for the item.
* @param item the item.
* @return the gem.
*/
public static Gem forItem(final Item item) {
for (Gem gem : values()) {
if (gem.getGem().getId() == item.getId()) {
return gem;
}
}
return null;
}
}

View file

@ -1,144 +0,0 @@
/*
package core.game.node.entity.skill.fletching.items.gem;
import org.crandor.game.node.item.Item;
*/
/**
* Represents a gem bolt.
* @author 'Vexia
* @date 01/12/2013
*//*
public enum GemBolt {
OPAL(new Item(877, 10), new Item(45, 10), new Item(879, 10), 11, 1.5),
PEARL(new Item(9140, 10), new Item(46, 10), new Item(880, 10), 41, 3.2),
JADE(new Item(9139, 10), new Item(9187, 10), new Item(9335, 10), 26, 2.4),
RED_TOPAZ(new Item(9141, 10), new Item(9188, 10), new Item(9336, 10), 48, 3.9),
SAPPHIRE(new Item(9142, 10), new Item(9189, 10), new Item(9337, 10), 56, 4),
EMERALD(new Item(9142, 10), new Item(9190, 10), new Item(9338, 10), 58, 5.5),
RUBY(new Item(9143, 10), new Item(9191, 10), new Item(9339, 10), 63, 6.3),
DIAMOND(new Item(9143, 10), new Item(9192, 10), new Item(9340, 10), 65, 7),
DRAGONSTONE(new Item(9144, 10), new Item(9193, 10), new Item(9341, 10), 71, 8.2),
ONYX(new Item(9144, 10), new Item(9194, 10), new Item(9342, 10), 73, 9.4);
*/
/**
* Constructs a new {@code GemBolt} {@code Object}.
* @param base the base.
* @param tip the tip.
* @param level the level.
* @param experience the experience.
*//*
GemBolt(Item base, Item tip, Item product, int level, double experience) {
this.base = base;
this.tip = tip;
this.product = product;
this.level = level;
this.experience = experience;
}
*/
/**
* Represents the base item.
*//*
private final Item base;
*/
/**
* Represents the tip to attach.
*//*
private final Item tip;
*/
/**
* Represents the product.
*//*
private final Item product;
*/
/**
* Represents the level.
*//*
private final int level;
*/
/**
* Represents the experience.
*//*
private final double experience;
*/
/**
* Gets the base.
* @return The base.
*//*
public Item getBase() {
return base;
}
*/
/**
* Gets the tip.
* @return The tip.
*//*
public Item getTip() {
return tip;
}
*/
/**
* 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;
}
*/
/**
* Method used to get the gem bolt from the id.
* @param boltt the boltt.
* @param tip the tip.
* @return the bolt.
*//*
public static GemBolt forItems(final Item boltt, final Item tip) {
for (GemBolt bolt : values()) {
if (bolt.getBase().getId() == boltt.getId() && bolt.getTip().getId() == tip.getId() || bolt.getBase().getId() == tip.getId() && bolt.getTip().getId() == boltt.getId()) {
return bolt;
}
}
return null;
}
}
*/

View file

@ -1,68 +0,0 @@
package content.global.skill.fletching.items.gem
import core.game.node.entity.player.Player
import core.game.node.entity.skill.SkillPulse
import core.game.node.entity.skill.Skills
import content.global.skill.fletching.Fletching.GemBolts
import core.game.node.item.Item
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items
/**
* Represents the gem cutting pulse(gem to bolt).
* @author Ceikry
*/
class GemBoltCutPulse
/**
* Constructs a new `GemCutPulse.java` `Object`.
* @param player the player.
* @param node the node.
* @param amount the amount.
*/(player: Player?, node: Item?,
/**
* Represents the gem we're cutting.
*/
private val gem: GemBolts,
/**
* Represents the amount to make.
*/
private var amount: Int) : SkillPulse<Item?>(player, node) {
/**
* Represents the ticks passed.
*/
private var ticks = 0
override fun checkRequirements(): Boolean {
if (player.skills.getLevel(Skills.FLETCHING) < gem.level) {
player.dialogueInterpreter.sendDialogue("You need a Fletching level of " + gem.level + " or above to do that.")
return false
}
return player.inventory.containsItem(Item(gem.gem))
}
override fun animate() {
if (ticks % 6 == 0) {
player.animate(ANIMATION)
}
}
override fun reward(): Boolean {
if (++ticks % 5 != 0) {
return false
}
val reward = if (gem.gem == Items.OYSTER_PEARLS_413) Item(gem.tip, 24) else Item(gem.tip, 12)
if (player.inventory.remove(Item(gem.gem))) {
player.inventory.add(reward)
player.skills.addExperience(Skills.FLETCHING, gem.experience, true)
}
amount--
return amount <= 0
}
companion object {
/**
* Represents the cutting animation.
*/
private val ANIMATION = Animation(6702)
}
}

View file

@ -1,91 +0,0 @@
package content.global.skill.fletching.items.gem;
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;
/**
* Represents the attaching of a gem bolt to a premade bolt.
* @author Ceikry
*/
public final class GemBoltPulse extends SkillPulse<Item> {
/**
* Represents the gem bolt being made.
*/
private Fletching.GemBolts bolt;
/**
* Represents the sets to make.
*/
private int sets = 0;
/**
* Represents the ticks passed.
*/
private int ticks;
/**
* Constructs a new {@code GemBoltPulse} {@code Object}.
* @param player the player.
* @param node the node.
* @param sets the sets.
*/
public GemBoltPulse(Player player, Item node, Fletching.GemBolts bolt, int sets) {
super(player, node);
this.bolt = bolt;
this.sets = sets;
}
@Override
public boolean checkRequirements() {
if (player.getSkills().getLevel(Skills.FLETCHING) < bolt.level) {
player.getDialogueInterpreter().sendDialogue("You need a Fletching level of " + bolt.level + " or above to do that.");
return false;
}
if (!player.getInventory().containsItem(new Item(bolt.base)) || !player.getInventory().containsItem(new Item(bolt.tip))) {
return false;
}
if (!player.getInventory().hasSpaceFor(new Item(bolt.product))){
player.getDialogueInterpreter().sendDialogue("You do not have enough inventory space.");
return false;
}
return true;
}
@Override
public void animate() {
}
@Override
public boolean reward() {
if (++ticks % 3 != 0) {
return false;
}
int baseAmount = player.getInventory().getAmount(bolt.base);
int tipAmount = player.getInventory().getAmount(bolt.tip);
Item base = new Item(bolt.base);
Item tip = new Item(bolt.tip);
Item product = new Item(bolt.product);
if(baseAmount >= 10 && tipAmount >= 10){
base.setAmount(10);
tip.setAmount(10);
product.setAmount(10);
} else {
int amount = baseAmount > tipAmount ? tipAmount : baseAmount;
base.setAmount(amount);
tip.setAmount(amount);
product.setAmount(amount);
}
if (player.getInventory().remove(base,tip)) {
player.getInventory().add(product);
player.getSkills().addExperience(Skills.FLETCHING, bolt.experience * product.getAmount(), true);
player.getPacketDispatch().sendMessage(product.getAmount() == 1 ? "You attach the tip to the bolt." : "You fletch " + product.getAmount() + " bolts.");
}
sets--;
return sets <= 0;
}
}

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