mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-28 05:45:10 -06:00
Merge branch 2009scape:master into master
This commit is contained in:
commit
6105a32d0f
141 changed files with 6551 additions and 915 deletions
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
<ObjectAction mode="remove" id="356" x="2804" y="3428" z="0"/>
|
||||
<ObjectAction mode="add" id="356" x="2804" y="3428" z="0"/>
|
||||
<ObjectAction mode="add" id="2783" x="3229" y="3254" z="0"/>
|
||||
|
||||
<ObjectAction mode="remove" id="20228" x="2537" y="3573" z="0"/>
|
||||
<ObjectAction mode="add" id="27663" x="2537" y="3573" z="0" direction="w"/>
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@
|
|||
},
|
||||
{
|
||||
"id": "2054",
|
||||
"replaceId": "24375",
|
||||
"replaceId": "2055",
|
||||
"fence": "false",
|
||||
"metal": "false"
|
||||
},
|
||||
|
|
@ -419,18 +419,6 @@
|
|||
"fence": "false",
|
||||
"metal": "false"
|
||||
},
|
||||
{
|
||||
"id": "2391",
|
||||
"replaceId": "1561",
|
||||
"fence": "false",
|
||||
"metal": "true"
|
||||
},
|
||||
{
|
||||
"id": "2392",
|
||||
"replaceId": "1561",
|
||||
"fence": "false",
|
||||
"metal": "true"
|
||||
},
|
||||
{
|
||||
"id": "2397",
|
||||
"replaceId": "24383",
|
||||
|
|
@ -2169,7 +2157,7 @@
|
|||
},
|
||||
{
|
||||
"id": "24376",
|
||||
"replaceId": "24377",
|
||||
"replaceId": "24375",
|
||||
"fence": "false",
|
||||
"metal": "false"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@
|
|||
},
|
||||
{
|
||||
"item_id": "401",
|
||||
"loc_data": "{1,2959,2959,0,160}-{1,2943,2950,0,160}-{1,2866,2976,0,160}-{1,2854,2977,0,160}-{1,2839,2975,0,160}-{1,2757,2947,0,160}-{1,2755,2952,0,160}-{1,2786,2960,0,160}-{1,2782,2987,0,160}-{1,2771,2995,0,160}-{1,2761,3000,0,160}-{1,3829,3053,0,160}-{1,3813,3066,0,160}-{1,3814,3064,0,160}-{1,3808,3060,0,160}-{1,2945,3068,0,160}-{1,2975,3013,0,160}-{1,2955,3010,0,160}-{1,2755,3008,0,160}-{1,2754,3017,0,160}-{1,2756,3060,0,160}-{1,2755,3070,0,160}-{1,2896,3119,0,160}-{1,2914,3111,0,160}-{1,2926,3110,0,160}-{1,2939,3102,0,160}-{1,2938,3073,0,160}-{1,2756,3074,0,160}-{1,2806,3128,0,160}-{1,2764,3131,0,160}-{1,2753,3125,0,160}-{1,2757,3109,0,160}-{1,2864,3195,0,160}-{1,2810,3387,0,160}-{1,2809,3388,0,160}-{1,2809,3385,0,160}-{1,2807,3386,0,160}-{1,2804,3385,0,160}-{1,2807,3384,0,160}-{1,2805,3384,0,160}-{1,2802,3382,0,160}-{1,2805,3381,0,160}-{1,2804,3380,0,160}-{1,2693,3727,0,160}-{1,2698,3729,0,160}-{1,2700,3731,0,160}-{1,2708,3728,0,160}-{1,2725,3731,0,160}-{1,2721,3730,0,160}-{1,2719,3733,0,160}-{1,2714,3733,0,160}-{1,2712,3732,0,160}-"
|
||||
"loc_data": "{1,2959,2959,0,160}-{1,2866,2976,0,160}-{1,2854,2977,0,160}-{1,2839,2975,0,160}-{1,2943,2950,0,160}-{1,2757,2947,0,160}-{1,2755,2952,0,160}-{1,2786,2960,0,160}-{1,2782,2987,0,160}-{1,2771,2995,0,160}-{1,2761,3000,0,160}-{1,2945,3068,0,160}-{1,2975,3013,0,160}-{1,2955,3010,0,160}-{1,3829,3053,0,160}-{1,3813,3066,0,160}-{1,3814,3064,0,160}-{1,3808,3060,0,160}-{1,2755,3008,0,160}-{1,2754,3017,0,160}-{1,2756,3060,0,160}-{1,2755,3070,0,160}-{1,2896,3119,0,160}-{1,2914,3111,0,160}-{1,2926,3110,0,160}-{1,2939,3102,0,160}-{1,2938,3073,0,160}-{1,2756,3074,0,160}-{1,2806,3128,0,160}-{1,2764,3131,0,160}-{1,2753,3125,0,160}-{1,2757,3109,0,160}-{1,2864,3195,0,160}-{1,2810,3387,0,160}-{1,2809,3388,0,160}-{1,2809,3385,0,160}-{1,2807,3386,0,160}-{1,2804,3385,0,160}-{1,2807,3384,0,160}-{1,2805,3384,0,160}-{1,2802,3382,0,160}-{1,2805,3381,0,160}-{1,2804,3380,0,160}-{1,2693,3727,0,160}-{1,2698,3729,0,160}-{1,2700,3731,0,160}-{1,2708,3728,0,160}-{1,2725,3731,0,160}-{1,2721,3730,0,160}-{1,2719,3733,0,160}-{1,2714,3733,0,160}-{1,2712,3732,0,160}-"
|
||||
},
|
||||
{
|
||||
"item_id": "444",
|
||||
|
|
@ -369,7 +369,7 @@
|
|||
},
|
||||
{
|
||||
"item_id": "1573",
|
||||
"loc_data": "{1,2559,2975,0,10}-{1,2560,2974,0,10}-{1,2561,2973,0,10}-{1,2561,2976,0,10}-{1,2740,3637,0,30}-{1,2736,3638,0,30}-{1,2735,3636,0,30}-{1,2743,3636,0,30}-{1,2739,3634,0,30}-{1,2743,3640,0,30}-{1,2741,3639,0,30}-{1,2736,3641,0,30}-{1,2738,3641,0,30}-{1,2734,3640,0,30}-{1,2738,3636,0,30}-"
|
||||
"loc_data": "{1,2560,2974,0,10}-{1,2561,2973,0,10}-{1,2561,2976,0,10}-{1,2559,2975,0,10}-{1,2740,3637,0,30}-{1,2736,3638,0,30}-{1,2735,3636,0,30}-{1,2743,3636,0,30}-{1,2739,3634,0,30}-{1,2743,3640,0,30}-{1,2741,3639,0,30}-{1,2736,3641,0,30}-{1,2738,3641,0,30}-{1,2734,3640,0,30}-{1,2738,3636,0,30}-"
|
||||
},
|
||||
{
|
||||
"item_id": "1590",
|
||||
|
|
@ -469,7 +469,7 @@
|
|||
},
|
||||
{
|
||||
"item_id": "1931",
|
||||
"loc_data": "{1,3209,3214,0,80}-{1,3166,3310,0,90}-{1,3144,3449,2,90}-"
|
||||
"loc_data": "{1,3209,3214,0,80}-{1,3166,3310,0,90}-{1,3144,3449,2,90}-{1,2918,10191,1,90}-"
|
||||
},
|
||||
{
|
||||
"item_id": "1935",
|
||||
|
|
|
|||
|
|
@ -17702,7 +17702,7 @@
|
|||
{
|
||||
"shop_price": "350",
|
||||
"ge_buy_limit": "5000",
|
||||
"examine": "A plain gold amulet.",
|
||||
"examine": "It needs a string so I can wear it.",
|
||||
"grand_exchange_price": "172",
|
||||
"durability": null,
|
||||
"name": "Gold amulet",
|
||||
|
|
@ -17722,7 +17722,7 @@
|
|||
{
|
||||
"shop_price": "810",
|
||||
"ge_buy_limit": "5000",
|
||||
"examine": "I wonder if I can get this enchanted.",
|
||||
"examine": "It needs a string so I can wear it.",
|
||||
"grand_exchange_price": "473",
|
||||
"durability": null,
|
||||
"name": "Sapphire amulet",
|
||||
|
|
@ -17742,7 +17742,7 @@
|
|||
{
|
||||
"shop_price": "1147",
|
||||
"ge_buy_limit": "5000",
|
||||
"examine": "I wonder if I can get this enchanted.",
|
||||
"examine": "It needs a string so I can wear it.",
|
||||
"grand_exchange_price": "695",
|
||||
"durability": null,
|
||||
"name": "Emerald amulet",
|
||||
|
|
@ -17762,7 +17762,7 @@
|
|||
{
|
||||
"shop_price": "2025",
|
||||
"ge_buy_limit": "5000",
|
||||
"examine": "I wonder if I can get this enchanted.",
|
||||
"examine": "It needs a string so I can wear it.",
|
||||
"grand_exchange_price": "1268",
|
||||
"durability": null,
|
||||
"name": "Ruby amulet",
|
||||
|
|
@ -17783,7 +17783,7 @@
|
|||
{
|
||||
"shop_price": "3307",
|
||||
"ge_buy_limit": "5000",
|
||||
"examine": "I wonder if I can get this enchanted.",
|
||||
"examine": "It needs a string so I can wear it.",
|
||||
"grand_exchange_price": "3359",
|
||||
"durability": null,
|
||||
"name": "Diamond amulet",
|
||||
|
|
@ -17802,7 +17802,7 @@
|
|||
},
|
||||
{
|
||||
"ge_buy_limit": "100",
|
||||
"examine": "I wonder if I can get this enchanted.",
|
||||
"examine": "It needs a string so I can wear it.",
|
||||
"grand_exchange_price": "26900",
|
||||
"durability": null,
|
||||
"name": "Dragonstone ammy",
|
||||
|
|
@ -48457,14 +48457,14 @@
|
|||
},
|
||||
{
|
||||
"shop_price": "200",
|
||||
"examine": "An acorn has been sown and watered in this plant pot.",
|
||||
"examine": "An acorn has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Oak seedling",
|
||||
"archery_ticket_price": "0",
|
||||
"id": "5358"
|
||||
},
|
||||
{
|
||||
"examine": "A willow tree seed has been sown and watered in this plant pot",
|
||||
"examine": "A willow tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Willow seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -48472,7 +48472,7 @@
|
|||
"id": "5359"
|
||||
},
|
||||
{
|
||||
"examine": "A maple tree has been sown (and watered) in this plant pot.",
|
||||
"examine": "A maple tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Maple seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -48480,7 +48480,7 @@
|
|||
"id": "5360"
|
||||
},
|
||||
{
|
||||
"examine": "A yew seed has been sown in the plant pot",
|
||||
"examine": "A yew tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Yew seedling",
|
||||
"weight": "1",
|
||||
|
|
@ -48488,7 +48488,7 @@
|
|||
"id": "5361"
|
||||
},
|
||||
{
|
||||
"examine": "A magic tree seed has been sown and watered in this plant pot.",
|
||||
"examine": "A magic tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Magic seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -48496,7 +48496,7 @@
|
|||
"id": "5362"
|
||||
},
|
||||
{
|
||||
"examine": "A spirit seedling is growing in this pot. It has been sown and watered.",
|
||||
"examine": "A spirit tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Spirit seedling",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -48511,7 +48511,7 @@
|
|||
"id": "5364"
|
||||
},
|
||||
{
|
||||
"examine": "A willow tree seed has been sown and watered in this plant pot",
|
||||
"examine": "A willow tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Willow seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -48519,7 +48519,7 @@
|
|||
"id": "5365"
|
||||
},
|
||||
{
|
||||
"examine": "A maple tree has been sown (and watered) in this plant pot.",
|
||||
"examine": "A maple tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Maple seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -48527,7 +48527,7 @@
|
|||
"id": "5366"
|
||||
},
|
||||
{
|
||||
"examine": "A yew seed has been sown in the plant pot",
|
||||
"examine": "A yew tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Yew seedling",
|
||||
"weight": "1",
|
||||
|
|
@ -48543,7 +48543,7 @@
|
|||
"id": "5368"
|
||||
},
|
||||
{
|
||||
"examine": "A spirit seedling is growing in this pot. It has been sown and watered.",
|
||||
"examine": "A spirit tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Spirit seedling",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -49497,7 +49497,7 @@
|
|||
"id": "5479"
|
||||
},
|
||||
{
|
||||
"examine": "An apple tree has been sown (and watered) in this plant pot.",
|
||||
"examine": "An apple tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Apple seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49505,7 +49505,7 @@
|
|||
"id": "5480"
|
||||
},
|
||||
{
|
||||
"examine": "A banana tree seed has been sown and watered in this plant pot.",
|
||||
"examine": "A banana tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Banana seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49513,14 +49513,14 @@
|
|||
"id": "5481"
|
||||
},
|
||||
{
|
||||
"examine": "An orange tree seed has been sown (and watered)in this plant pot.",
|
||||
"examine": "An orange tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Orange seedling",
|
||||
"archery_ticket_price": "0",
|
||||
"id": "5482"
|
||||
},
|
||||
{
|
||||
"examine": "A curry seed has been sown and watered in this plant pot.",
|
||||
"examine": "A curry tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Curry seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49528,7 +49528,7 @@
|
|||
"id": "5483"
|
||||
},
|
||||
{
|
||||
"examine": "A pineapple tree has been sown (and watered) in this plant pot.",
|
||||
"examine": "A pineapple plant seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Pineapple seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49536,7 +49536,7 @@
|
|||
"id": "5484"
|
||||
},
|
||||
{
|
||||
"examine": "This needs watering before it will grow.",
|
||||
"examine": "A papaya tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Papaya seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49544,7 +49544,7 @@
|
|||
"id": "5485"
|
||||
},
|
||||
{
|
||||
"examine": "This sapling is ready to be replanted in a fruit tree patch.",
|
||||
"examine": "A palm tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Palm seedling",
|
||||
"weight": "0.66",
|
||||
|
|
@ -49552,7 +49552,7 @@
|
|||
"id": "5486"
|
||||
},
|
||||
{
|
||||
"examine": "A calquat tree has been sown (and watered) in this plant pot.",
|
||||
"examine": "A calquat tree seed has been sown in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Calquat seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49560,7 +49560,7 @@
|
|||
"id": "5487"
|
||||
},
|
||||
{
|
||||
"examine": "An apple tree has been sown (and watered) in this plant pot.",
|
||||
"examine": "An apple tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Apple seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49576,14 +49576,14 @@
|
|||
"id": "5489"
|
||||
},
|
||||
{
|
||||
"examine": "An orange tree seed has been sown (and watered)in this plant pot.",
|
||||
"examine": "An orange tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Orange seedling",
|
||||
"archery_ticket_price": "0",
|
||||
"id": "5490"
|
||||
},
|
||||
{
|
||||
"examine": "A curry seed has been sown and watered in this plant pot.",
|
||||
"examine": "A curry tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Curry seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49591,7 +49591,7 @@
|
|||
"id": "5491"
|
||||
},
|
||||
{
|
||||
"examine": "A pineapple tree has been sown (and watered) in this plant pot.",
|
||||
"examine": "A pineapple plant seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Pineapple seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49599,7 +49599,7 @@
|
|||
"id": "5492"
|
||||
},
|
||||
{
|
||||
"examine": "This needs watering before it will grow.",
|
||||
"examine": "A papaya tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Papaya seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -49607,7 +49607,7 @@
|
|||
"id": "5493"
|
||||
},
|
||||
{
|
||||
"examine": "This sapling is ready to be replanted in a fruit tree patch.",
|
||||
"examine": "A palm tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Palm seedling",
|
||||
"weight": "0.66",
|
||||
|
|
@ -49615,7 +49615,7 @@
|
|||
"id": "5494"
|
||||
},
|
||||
{
|
||||
"examine": "A calquat tree has been sown (and watered) in this plant pot.",
|
||||
"examine": "A calquat tree seed has been sown and watered in this plant pot.",
|
||||
"durability": null,
|
||||
"name": "Calquat seedling",
|
||||
"weight": "0.9",
|
||||
|
|
@ -51571,9 +51571,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "483",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Iron dagger(p+)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51600,9 +51600,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "350",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Bronze dagger(p+)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51630,9 +51630,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "575",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Steel dagger(p+)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51660,9 +51660,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "538",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Mithril dagger(p+)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51686,9 +51686,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "800",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Adamant dagger(p+)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51783,9 +51783,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "664",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Black dagger(p+)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51831,9 +51831,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "4128",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Iron dagger(p++)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51860,9 +51860,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "5088",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Br'ze dagger(p++)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51890,9 +51890,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "4729",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Steel dagger(p++)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51920,9 +51920,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "3476",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Mithril dagger(p++)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51946,9 +51946,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "4612",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Adamant dagger(p++)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -51976,10 +51976,10 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"lendable": "true",
|
||||
"grand_exchange_price": "5061",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Rune dagger(p++)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -52043,9 +52043,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "4921",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "Black dagger(p++)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -52751,7 +52751,7 @@
|
|||
},
|
||||
{
|
||||
"ge_buy_limit": "100",
|
||||
"examine": "This looks a good deal stronger than normal dwarven stout.",
|
||||
"examine": "This looks a good deal stronger than normal Dwarven Stout.",
|
||||
"grand_exchange_price": "56400",
|
||||
"durability": null,
|
||||
"name": "Dwarven stout(m)",
|
||||
|
|
@ -53779,7 +53779,7 @@
|
|||
},
|
||||
{
|
||||
"ge_buy_limit": "100",
|
||||
"examine": "This looks a good deal stronger than normal dwarven stout.",
|
||||
"examine": "This looks a good deal stronger than normal Dwarven Stout.",
|
||||
"grand_exchange_price": "44600",
|
||||
"durability": null,
|
||||
"name": "Dwarven stout(m1)",
|
||||
|
|
@ -53798,7 +53798,7 @@
|
|||
},
|
||||
{
|
||||
"ge_buy_limit": "100",
|
||||
"examine": "This looks a good deal stronger than normal dwarven stout.",
|
||||
"examine": "This looks a good deal stronger than normal Dwarven Stout.",
|
||||
"grand_exchange_price": "68900",
|
||||
"durability": null,
|
||||
"name": "Dwarven stout(m2)",
|
||||
|
|
@ -53817,7 +53817,7 @@
|
|||
},
|
||||
{
|
||||
"ge_buy_limit": "100",
|
||||
"examine": "This looks a good deal stronger than normal dwarven stout.",
|
||||
"examine": "This looks a good deal stronger than normal Dwarven Stout.",
|
||||
"grand_exchange_price": "98800",
|
||||
"durability": null,
|
||||
"name": "Dwarven stout(m3)",
|
||||
|
|
@ -53836,7 +53836,7 @@
|
|||
},
|
||||
{
|
||||
"ge_buy_limit": "100",
|
||||
"examine": "This looks a good deal stronger than normal dwarven stout.",
|
||||
"examine": "This looks a good deal stronger than normal Dwarven Stout.",
|
||||
"grand_exchange_price": "237400",
|
||||
"durability": null,
|
||||
"name": "Dwarven stout(m4)",
|
||||
|
|
@ -60136,7 +60136,7 @@
|
|||
"id": "6578"
|
||||
},
|
||||
{
|
||||
"examine": "I wonder if I can get this enchanted.",
|
||||
"examine": "It needs a string so I can wear it.",
|
||||
"grand_exchange_price": "9058124",
|
||||
"durability": null,
|
||||
"name": "Onyx amulet",
|
||||
|
|
@ -60318,9 +60318,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "497",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "White dagger(p)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -60348,9 +60348,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "982",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "White dagger(p+)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -60378,9 +60378,9 @@
|
|||
"equip_audio": "2248",
|
||||
"render_anim": "2584",
|
||||
"equipment_slot": "3",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"grand_exchange_price": "8422",
|
||||
"attack_audios": "2517,2517,2500,2517",
|
||||
"attack_anims": "400,400,401,400",
|
||||
"name": "White dagger(p++)",
|
||||
"tradeable": "true",
|
||||
"archery_ticket_price": "0",
|
||||
|
|
@ -132205,5 +132205,15 @@
|
|||
"examine": "Emperor of all he surveys.",
|
||||
"name": "Penguin",
|
||||
"id": "12764"
|
||||
},
|
||||
{
|
||||
"examine": "This key opens a chest in the Carnillean household.",
|
||||
"name": "Chest key",
|
||||
"id": "709"
|
||||
},
|
||||
{
|
||||
"examine": "This stuff looks nasty.",
|
||||
"name": "Poison",
|
||||
"id": "273"
|
||||
}
|
||||
]
|
||||
|
|
@ -887,6 +887,10 @@
|
|||
"region": "10390",
|
||||
"id": "102"
|
||||
},
|
||||
{
|
||||
"region": "10391",
|
||||
"id": "478"
|
||||
},
|
||||
{
|
||||
"region": "10393",
|
||||
"id": "346"
|
||||
|
|
|
|||
|
|
@ -9386,6 +9386,7 @@
|
|||
"attack_level": "1"
|
||||
},
|
||||
{
|
||||
"examine": "On special duty to protect the Canilleans.",
|
||||
"name": "Guard",
|
||||
"defence_level": "1",
|
||||
"safespot": null,
|
||||
|
|
@ -9398,18 +9399,19 @@
|
|||
"attack_level": "1"
|
||||
},
|
||||
{
|
||||
"examine": "A member of the Hazeel cult.",
|
||||
"melee_animation": "811",
|
||||
"respawn_delay": "60",
|
||||
"defence_animation": "425",
|
||||
"death_animation": "836",
|
||||
"name": "Clivet",
|
||||
"defence_level": "1",
|
||||
"safespot": null,
|
||||
"lifepoints": "25",
|
||||
"melee_animation": "811",
|
||||
"strength_level": "1",
|
||||
"id": "893",
|
||||
"range_level": "1",
|
||||
"respawn_delay": "60",
|
||||
"attack_level": "1",
|
||||
"defence_animation": "425"
|
||||
"attack_level": "1"
|
||||
},
|
||||
{
|
||||
"examine": "A member of the Hazeel cult.",
|
||||
|
|
@ -44609,7 +44611,9 @@
|
|||
"strength_level": "1",
|
||||
"id": "5079",
|
||||
"range_level": "1",
|
||||
"attack_level": "1"
|
||||
"attack_level": "1",
|
||||
"death_animation": "5183",
|
||||
"examine": "Handle with care."
|
||||
},
|
||||
{
|
||||
"examine": "It looks fluffy and cute; it's probably deadly.",
|
||||
|
|
@ -59158,7 +59162,7 @@
|
|||
"magic_animation": "7515",
|
||||
"death_animation": "7461",
|
||||
"name": "Revenant hellhound",
|
||||
"defence_level": "1",
|
||||
"defence_level": "50",
|
||||
"safespot": null,
|
||||
"lifepoints": "80",
|
||||
"strength_level": "50",
|
||||
|
|
@ -60004,7 +60008,7 @@
|
|||
"magic_animation": "7515",
|
||||
"death_animation": "7461",
|
||||
"name": "Revenant hellhound",
|
||||
"defence_level": "1",
|
||||
"defence_level": "50",
|
||||
"safespot": null,
|
||||
"lifepoints": "80",
|
||||
"strength_level": "50",
|
||||
|
|
@ -74213,6 +74217,7 @@
|
|||
"id": "880"
|
||||
},
|
||||
{
|
||||
"examine": "The Carnillean family chef.",
|
||||
"name": "Claus the chef",
|
||||
"id": "886"
|
||||
},
|
||||
|
|
@ -74221,14 +74226,17 @@
|
|||
"id": "888"
|
||||
},
|
||||
{
|
||||
"examine": "Ceril Carnillean's wife.",
|
||||
"name": "Henryeta Carnillean",
|
||||
"id": "889"
|
||||
},
|
||||
{
|
||||
"examine": "The Carnellian family butler.",
|
||||
"name": "Butler Jones",
|
||||
"id": "890"
|
||||
},
|
||||
{
|
||||
"examine": "Leader of the Hazeel cult.",
|
||||
"name": "Alomone",
|
||||
"id": "891"
|
||||
},
|
||||
|
|
@ -76127,6 +76135,7 @@
|
|||
"id": "2308"
|
||||
},
|
||||
{
|
||||
"examine": "In his former life an expert brewer.",
|
||||
"name": "Metarialus",
|
||||
"id": "2322"
|
||||
},
|
||||
|
|
@ -77684,6 +77693,7 @@
|
|||
"id": "3311"
|
||||
},
|
||||
{
|
||||
"examine": "They're going to sing to you!",
|
||||
"name": "Zanaris choir",
|
||||
"id": "3312"
|
||||
},
|
||||
|
|
@ -83520,6 +83530,7 @@
|
|||
"id": "6741"
|
||||
},
|
||||
{
|
||||
"examine": "A traditional snowman.",
|
||||
"name": "Snowman",
|
||||
"id": "6746"
|
||||
},
|
||||
|
|
@ -87757,6 +87768,7 @@
|
|||
"id": "8516"
|
||||
},
|
||||
{
|
||||
"examine": "The rebellious teenage son of the Queen of Snow. Interesting outfit!",
|
||||
"name": "Jack Frost",
|
||||
"id": "8517"
|
||||
},
|
||||
|
|
@ -87789,6 +87801,7 @@
|
|||
"id": "8539"
|
||||
},
|
||||
{
|
||||
"examine": "A jolly-looking present-distributor.",
|
||||
"name": "Santa Claus",
|
||||
"id": "8540"
|
||||
},
|
||||
|
|
@ -88188,5 +88201,10 @@
|
|||
"examine": "Angry and stressed, he doesn't seem to have time for anything other than sand.",
|
||||
"name": "Sandy",
|
||||
"id": "3112"
|
||||
},
|
||||
{
|
||||
"examine": "I hope she can find all the ingredients!",
|
||||
"name": "Cook",
|
||||
"id": "4239"
|
||||
}
|
||||
]
|
||||
|
|
@ -2573,7 +2573,11 @@
|
|||
},
|
||||
{
|
||||
"npc_id": "885",
|
||||
"loc_data": "{2565,3271,0,0,0}-{2573,3268,1,0,0}-"
|
||||
"loc_data": "{2565,3271,0,1,0}-{2573,3268,1,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "886",
|
||||
"loc_data": "{2567,9669,0,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "887",
|
||||
|
|
@ -2581,15 +2585,27 @@
|
|||
},
|
||||
{
|
||||
"npc_id": "888",
|
||||
"loc_data": "{2565,3273,0,0,0}-"
|
||||
"loc_data": "{2565,3273,0,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "889",
|
||||
"loc_data": "{2571,3270,0,0,0}-"
|
||||
"loc_data": "{2571,3270,0,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "890",
|
||||
"loc_data": "{2569,3272,0,0,0}-{2573,3269,1,0,0}-"
|
||||
"loc_data": "{2569,3272,0,1,0}-{2573,3269,1,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "891",
|
||||
"loc_data": "{2609,9670,0,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "893",
|
||||
"loc_data": "{2567,9683,0,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "894",
|
||||
"loc_data": "{2609,9686,0,1,0}-{2607,9681,0,1,0}-{2609,9674,0,1,0}-{2608,9672,0,1,0}-{2604,9674,0,1,0}-{2605,9671,0,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "895",
|
||||
|
|
@ -5547,6 +5563,10 @@
|
|||
"npc_id": "2321",
|
||||
"loc_data": "{2915,10194,1,1,0}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "2322",
|
||||
"loc_data": "{3679,9957,0,1,1}-"
|
||||
},
|
||||
{
|
||||
"npc_id": "2323",
|
||||
"loc_data": "{3056,3306,0,1,1}-"
|
||||
|
|
|
|||
|
|
@ -19168,87 +19168,87 @@
|
|||
"ids": "13614"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Varrock",
|
||||
"examine": "A gateway to Varrock.",
|
||||
"ids": "13615"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Lumbridge",
|
||||
"examine": "A gateway to Lumbridge.",
|
||||
"ids": "13616"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Falador",
|
||||
"examine": "A gateway to Falador.",
|
||||
"ids": "13617"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Camelot",
|
||||
"examine": "A gateway to Camelot.",
|
||||
"ids": "13618"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Ardougne",
|
||||
"examine": "A gateway to Ardougne.",
|
||||
"ids": "13619"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to the Yanille watchtower",
|
||||
"examine": "A gateway to the Yanille watchtower.",
|
||||
"ids": "13620"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Trollheim",
|
||||
"examine": "A gateway to Kharyrll.",
|
||||
"ids": "13621"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Varrock",
|
||||
"examine": "A gateway to Varrock.",
|
||||
"ids": "13622"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Lumbridge",
|
||||
"examine": "A gateway to Lumbridge.",
|
||||
"ids": "13623"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Falador",
|
||||
"examine": "A gateway to Falador.",
|
||||
"ids": "13624"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Camelot",
|
||||
"examine": "A gateway to Camelot.",
|
||||
"ids": "13625"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Ardougne",
|
||||
"examine": "A gateway to Ardougne.",
|
||||
"ids": "13626"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to the Yanille watchtower",
|
||||
"examine": "A gateway to the Yanille watchtower.",
|
||||
"ids": "13627"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Trollheim",
|
||||
"examine": "A gateway to Kharyrll.",
|
||||
"ids": "13628"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Varrock",
|
||||
"examine": "A gateway to Varrock.",
|
||||
"ids": "13629"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Lumbridge",
|
||||
"examine": "A gateway to Lumbridge.",
|
||||
"ids": "13630"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Falador",
|
||||
"examine": "A gateway to Falador.",
|
||||
"ids": "13631"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Camelot",
|
||||
"examine": "A gateway to Camelot.",
|
||||
"ids": "13632"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Ardougne",
|
||||
"examine": "A gateway to Ardougne.",
|
||||
"ids": "13633"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to the Yanille watchtower",
|
||||
"examine": "A gateway to the Yanille watchtower.",
|
||||
"ids": "13634"
|
||||
},
|
||||
{
|
||||
"examine": "A gateway to Trollheim",
|
||||
"examine": "A gateway to Kharyrll.",
|
||||
"ids": "13635"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ import core.game.node.entity.player.Player
|
|||
import core.game.node.entity.player.link.TeleportManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.game.world.update.flag.context.Graphics
|
||||
import org.rs09.consts.Items
|
||||
import core.game.world.GameWorld.Pulser
|
||||
import core.tools.Log
|
||||
import org.rs09.consts.Sounds
|
||||
import java.util.*
|
||||
|
||||
|
|
@ -73,17 +75,17 @@ enum class EnchantedJewellery(
|
|||
),
|
||||
AMULET_OF_GLORY(
|
||||
arrayOf(
|
||||
"Edgeville.",
|
||||
"Karamja.",
|
||||
"Draynor Village.",
|
||||
"Al-Kharid.",
|
||||
"Nowhere."
|
||||
"Edgeville",
|
||||
"Karamja",
|
||||
"Draynor Village",
|
||||
"Al Kharid",
|
||||
"Nowhere"
|
||||
),
|
||||
arrayOf(
|
||||
Location.create(3087, 3495, 0),
|
||||
Location.create(2919, 3175, 0),
|
||||
Location.create(3104, 3249, 0),
|
||||
Location.create(3304, 3124, 0)
|
||||
Location.create(3292, 3175, 0)
|
||||
),
|
||||
Items.AMULET_OF_GLORY4_1712,
|
||||
Items.AMULET_OF_GLORY3_1710,
|
||||
|
|
@ -209,7 +211,7 @@ enum class EnchantedJewellery(
|
|||
constructor(options: Array<String>, locations: Array<Location>, vararg ids: Int) : this(options, locations, false, *ids)
|
||||
|
||||
/**
|
||||
* Method used when the player "Use"s the jewellery piece.
|
||||
* Method used when the player clicks one of the options in the jewellery piece's options dialog.
|
||||
* @param player the player.
|
||||
* @param item the used jewellery item.
|
||||
* @param buttonID the button id.
|
||||
|
|
@ -222,7 +224,7 @@ enum class EnchantedJewellery(
|
|||
}
|
||||
return
|
||||
}
|
||||
attemptTeleport(player, item, buttonID, isEquipped)
|
||||
attemptTeleport(player, item, buttonID, true, isEquipped)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -230,43 +232,69 @@ enum class EnchantedJewellery(
|
|||
* @param player the player.
|
||||
* @param item the used jewellery item.
|
||||
* @param buttonID the button id.
|
||||
* @param replace Whether to attempt replacing the item with its lower-charge version. Always true unless you generated a fake item in code.
|
||||
* @param isEquipped If the player is operating.
|
||||
*/
|
||||
fun attemptTeleport(player: Player, item: Item, buttonID: Int, isEquipped: Boolean): Boolean {
|
||||
fun attemptTeleport(player: Player, item: Item, buttonID: Int, replace: Boolean, isEquipped: Boolean): Boolean {
|
||||
val itemIndex = getItemIndex(item)
|
||||
val nextJewellery = Item(getNext(itemIndex))
|
||||
if (!canTeleport(player, nextJewellery)) {
|
||||
return false
|
||||
}
|
||||
Pulser.submit(object : Pulse(0) {
|
||||
private var count = 0
|
||||
private var location = getLocation(buttonID)
|
||||
override fun pulse(): Boolean {
|
||||
when (count) {
|
||||
0 -> {
|
||||
lock(player,4)
|
||||
visualize(player, ANIMATION, GRAPHICS)
|
||||
playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200)
|
||||
player.impactHandler.disabledTicks = 4
|
||||
closeInterface(player)
|
||||
val location = getLocation(buttonID)
|
||||
player.scripts.delay = GameWorld.ticks + 4
|
||||
queueScript(player, 0, QueueStrength.SOFT) { stage ->
|
||||
when (stage) {
|
||||
0 -> {
|
||||
lock(player, 4)
|
||||
visualize(player, ANIMATION, GRAPHICS)
|
||||
playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200)
|
||||
player.impactHandler.disabledTicks = 4
|
||||
closeInterface(player)
|
||||
return@queueScript delayScript(player, 3)
|
||||
}
|
||||
1 -> {
|
||||
teleport(player, location)
|
||||
resetAnimator(player)
|
||||
unlock(player)
|
||||
player.dispatch(TeleportEvent(TeleportManager.TeleportType.NORMAL, TeleportMethod.JEWELRY, item, location))
|
||||
if (!replace) {
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
3 -> {
|
||||
teleport(player,location)
|
||||
resetAnimator(player)
|
||||
if (isLastItemIndex(itemIndex)) {
|
||||
if (isCrumble) crumbleJewellery(player, item, isEquipped)
|
||||
} else {
|
||||
replaceJewellery(player, item, nextJewellery, isEquipped)
|
||||
if (isLastItemIndex(itemIndex)) {
|
||||
if (isSlayerRing(item)) {
|
||||
if (isEquipped) {
|
||||
// Remove it now, but only addItemOrDrop the gem after the teleport has procced
|
||||
if (removeItem(player, item, Container.EQUIPMENT)) {
|
||||
return@queueScript delayScript(player, 1)
|
||||
}
|
||||
log(this.javaClass, Log.ERR, "Error replacing slayer ring with enchanted gem (removeItem from equipment)")
|
||||
}
|
||||
// Not equipped -> can replace fluently
|
||||
if (replaceSlot(player, item.slot, Item(Items.ENCHANTED_GEM_4155), item) == item) {
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
log(this.javaClass, Log.ERR, "Error replacing slayer ring with enchanted gem (replaceSlot)")
|
||||
}
|
||||
unlock(player)
|
||||
player.dispatch(TeleportEvent(TeleportManager.TeleportType.NORMAL, TeleportMethod.JEWELRY, item, location))
|
||||
return true
|
||||
// Not slayer ring
|
||||
if (isCrumble) {
|
||||
val removeFrom = if (isEquipped) Container.EQUIPMENT else Container.INVENTORY
|
||||
if (removeItem(player, item, removeFrom)) {
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
log(this.javaClass, Log.ERR, "Error crumbling jewelry")
|
||||
}
|
||||
} else {
|
||||
replaceJewellery(player, item, nextJewellery, isEquipped)
|
||||
}
|
||||
}
|
||||
count += 1
|
||||
return false
|
||||
2 -> {
|
||||
addItemOrDrop(player, Items.ENCHANTED_GEM_4155)
|
||||
sendMessage(player, "Your Ring of Slaying reverts back into a regular enchanted gem.")
|
||||
}
|
||||
}
|
||||
})
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -278,21 +306,6 @@ enum class EnchantedJewellery(
|
|||
}
|
||||
}
|
||||
|
||||
private fun crumbleJewellery(player: Player, item: Item, isEquipped: Boolean) {
|
||||
if (isEquipped) {
|
||||
removeItem(player, item, Container.EQUIPMENT)
|
||||
} else {
|
||||
removeItem(player, item)
|
||||
}
|
||||
if (isSlayerRing(item)) {
|
||||
queueScript(player, 1, QueueStrength.SOFT) {
|
||||
addItemOrDrop(player, Items.ENCHANTED_GEM_4155)
|
||||
sendMessage(player, "Your Ring of Slaying reverts back into a regular enchanted gem.")
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSlayerRing(item: Item): Boolean {
|
||||
return (item.id in RING_OF_SLAYING.ids)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,8 +203,8 @@ public enum Consumables {
|
|||
ASGARNIAN_ALE(new Drink(new int[] {1905, 1919}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
|
||||
ASGARNIAN_ALE_POH(new Drink(new int[] {7744, 7742}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
|
||||
ASGARNIAN_ALE_KEG(new Drink(new int[] {5785, 5783, 5781, 5779, 5769}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), new Animation(2289), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
|
||||
ASGARNIAN_ALE_M(new Drink(new int[] {5739, 1919}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)))),
|
||||
ASGARNIAN_ALE_M_KEG(new Drink(new int[] {5865, 5863, 5861, 5859, 5769}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), new Animation(2289))),
|
||||
ASGARNIAN_ALE_M(new Drink(new int[] {5739, 1919}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
|
||||
ASGARNIAN_ALE_M_KEG(new Drink(new int[] {5865, 5863, 5861, 5859, 5769}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), new Animation(2289), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
|
||||
AXEMANS_FOLLY(new Drink(new int[] {5751, 1919}, new MultiEffect(new SkillEffect(Skills.WOODCUTTING, 1, 0), new HealingEffect(1), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.ATTACK, -3, 0)))),
|
||||
AXEMANS_FOLLY_KEG(new Drink(new int[] {5825, 5823, 5821, 5819, 5769}, new MultiEffect(new SkillEffect(Skills.WOODCUTTING, 1, 0), new HealingEffect(1), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.ATTACK, -3, 0)), new Animation(2289))),
|
||||
AXEMANS_FOLLY_M(new Drink(new int[] {5753, 1919}, new MultiEffect(new SkillEffect(Skills.WOODCUTTING, 2, 0), new HealingEffect(2), new SkillEffect(Skills.STRENGTH, -4, 0), new SkillEffect(Skills.ATTACK, -4, 0)))),
|
||||
|
|
@ -231,24 +231,26 @@ public enum Consumables {
|
|||
DRAGON_BITTER_M_KEG(new Drink(new int[] {5889, 5887, 5885, 5883, 5769}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), new Animation(2289))),
|
||||
DWARVEN_STOUT(new Drink(new int[] {1913, 1919}, new MultiEffect(new SkillEffect(Skills.MINING, 1, 0), new SkillEffect(Skills.SMITHING, 1 ,0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0), new SkillEffect(Skills.DEFENCE, -2, 0), new HealingEffect(1)), "You drink the Dwarven Stout. It tastes foul.", "It tastes pretty strong too.")),
|
||||
DWARVEN_STOUT_KEG(new Drink(new int[] {5777, 5775, 5773, 5771, 5769}, new MultiEffect(new SkillEffect(Skills.MINING, 1, 0), new SkillEffect(Skills.SMITHING, 1 ,0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0), new SkillEffect(Skills.DEFENCE, -2, 0), new HealingEffect(1)), new Animation(2289), "You drink the Dwarven Stout. It tastes foul.", "It tastes pretty strong too.")),
|
||||
DWARVEN_STOUT_M(new Drink(new int[] {5747, 1919}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)))),
|
||||
DWARVEN_STOUT_M_KEG(new Drink(new int[] {5857, 5855, 5853, 5851, 5769}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)), new Animation(2289))),
|
||||
DWARVEN_STOUT_M(new Drink(new int[] {5747, 1919}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)), "You drink the Dwarven Stout. It tastes foul.", "It tastes pretty strong too.")),
|
||||
DWARVEN_STOUT_M_KEG(new Drink(new int[] {5857, 5855, 5853, 5851, 5769}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)), new Animation(2289), "You drink the Dwarven Stout. It tastes foul.", "It tastes pretty strong too.")),
|
||||
GREENMANS_ALE(new Drink(new int[] {1909, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)))),
|
||||
GREENMANS_ALE_POH(new Drink(new int[] {7746, 7742}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)))),
|
||||
GREENMANS_ALE_KEG(new Drink(new int[] {5793, 5791, 5789, 5787, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)), new Animation(2289))),
|
||||
GREENMANS_ALE_M(new Drink(new int[] {5743, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))),
|
||||
GREENMANS_ALE_M_KEG(new Drink(new int[] {5873, 5871, 5869, 5867, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))),
|
||||
GROG(new Drink(new int[] {1915, 1919}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)))),
|
||||
MOONLIGHT_MEAD(new Drink(new int[] {2955, 1919}, new HealingEffect(4), "It tastes like something just died in your mouth.")),
|
||||
MOONLIGHT_MEAD_KEG(new Drink(new int[] {5817, 5815, 5813, 5811, 5769}, new HealingEffect(4), new Animation(2289), "It tastes like something just died in your mouth.")),
|
||||
MOONLIGHT_MEAD_M(new Drink(new int[] {5749, 1919}, new HealingEffect(6))),
|
||||
MOONLIGHT_MEAD_M_KEG(new Drink(new int[] {5897, 5895, 5893, 5891, 5769}, new HealingEffect(6), new Animation(2289))),
|
||||
MOONLIGHT_MEAD(new Drink(new int[] {2955, 1919}, new HealingEffect(5), "It tastes like something just died in your mouth.")),
|
||||
MOONLIGHT_MEAD_KEG(new Drink(new int[] {5817, 5815, 5813, 5811, 5769}, new HealingEffect(5), new Animation(2289), "It tastes like something just died in your mouth.")),
|
||||
MOONLIGHT_MEAD_M(new Drink(new int[] {5749, 1919}, new HealingEffect(4), "It tastes like something just died in your mouth.")),
|
||||
MOONLIGHT_MEAD_M_KEG(new Drink(new int[] {5897, 5895, 5893, 5891, 5769}, new HealingEffect(4), new Animation(2289), "It tastes like something just died in your mouth.")),
|
||||
SLAYERS_RESPITE(new Drink(new int[] {5759, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.SLAYER, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))),
|
||||
SLAYERS_RESPITE_KEG(new Drink(new int[] {5841, 5839, 5837, 5835, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.SLAYER, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))),
|
||||
SLAYERS_RESPITE_M(new Drink(new int[] {5761, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.SLAYER, 4, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))),
|
||||
SLAYERS_RESPITE_M_KEG(new Drink(new int[] {5841, 5839, 5837, 5835, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.SLAYER, 4, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))),
|
||||
WIZARDS_MIND_BOMB(new Drink(new int[] {1907, 1919}, new WizardsMindBombEffect(), "You drink the Wizard's Mind Bomb.", "You feel very strange.")),
|
||||
MATURE_WMB(new Drink(new int[] {5741, 1919}, new MatureWmbEffect())),
|
||||
WIZARDS_MIND_BOMB_KEG(new Drink(new int[] {5801, 5799, 5797, 5795, 5769}, new WizardsMindBombEffect(), new Animation(2289), "You drink the Wizard's Mind Bomb.", "You feel very strange.")),
|
||||
MATURE_WMB(new Drink(new int[] {5741, 1919}, new MatureWmbEffect(), "You drink the mature Wizard's Mind Bomb.", "You feel very strange.")),
|
||||
MATURE_WMB_KEG(new Drink(new int[] {5881, 5879, 5877, 5875, 5769}, new MatureWmbEffect(), new Animation(2289), "You drink the mature Wizard's Mind Bomb.", "You feel very strange.")),
|
||||
|
||||
/** Cocktails */
|
||||
FRUIT_BLAST(new Drink(new int[] {2084, 2026}, new HealingEffect(9))),
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
|
|||
"South-west Varrock mine" to Location.create(3176, 3362, 0), // South-west Varrock mine / Champion's Guild mine
|
||||
"Varrock east bank" to Location.create(3259, 3407, 0), // Varrock east bank / Rune shop
|
||||
"Lumbridge Swamp south-east mine" to Location.create(3227, 3150, 0), // Lumbridge Swamp south-east mine
|
||||
//"Burgh de Rott bank" to Location.create(3500, 3219, 0), // Burgh de Rott bank (requires quest to enter)
|
||||
"Burgh de Rott bank" to Location.create(3500, 3219, 0), // Burgh de Rott bank (requires quest to enter)
|
||||
"Canifis Bank" to Location.create(3504, 3487, 0), // Canifis bank
|
||||
"Mos Le'Harmless bank" to Location.create(3687, 2969, 0), // Mos Le'Harmless bank (requires quest to enter but is currently accessible for Slayer)
|
||||
"Gnome stronghold Bank" to Location.create(2460, 3432, 0), // Gnome stronghold bank
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com
|
|||
fun teleportToStar(player: Player) {
|
||||
val condition: (p: Player) -> Boolean = when (star.location.toLowerCase()) {
|
||||
"canifis bank" -> {p -> requireQuest(p, Quests.PRIEST_IN_PERIL, "to access this.")}
|
||||
//"burgh de rott bank" -> {p -> hasRequirement(p, Quests.IN_AID_OF_THE_MYREQUE)} //disabled: crash
|
||||
"burgh de rott bank" -> {p -> hasRequirement(p, Quests.IN_AID_OF_THE_MYREQUE)}
|
||||
"crafting guild" -> {p -> hasLevelStat(p, Skills.CRAFTING, 40)}
|
||||
"lletya bank" -> {p -> hasRequirement(p, Quests.MOURNINGS_END_PART_I)}
|
||||
"jatizso mine" -> {p -> hasRequirement(p, Quests.THE_FREMENNIK_ISLES)}
|
||||
|
|
@ -120,7 +120,7 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com
|
|||
1 -> dialogue("WARNING: The star is located in the wilderness.").also { stage++ }
|
||||
2 -> player.dialogueInterpreter.sendOptions("Teleport to the star?", "Yes", "No").also { stage++ }
|
||||
3 -> when (buttonID) {
|
||||
1 -> end().also { teleportToStar(player) }
|
||||
1 -> teleportToStar(player)
|
||||
2 -> end()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class EmptyOptionListener : InteractionListener {
|
|||
BUCKET_OF_COMPOST(Items.COMPOST_6032, Items.BUCKET_1925, "You empty the bucket of compost."),
|
||||
BUCKET_OF_SUPERCOMPOST(Items.SUPERCOMPOST_6034, Items.BUCKET_1925, "You empty the bucket of supercompost."),
|
||||
BUCKET_OF_SLIME(Items.BUCKET_OF_SLIME_4286, Items.BUCKET_1925, "You empty the contents of the bucket on the floor.", Sounds.LIQUID_2401),
|
||||
APPLE_MUSH(Items.APPLE_MUSH_5992,Items.BUCKET_1925, "You empty the bucket of apple mush."),
|
||||
VIAL_OF_WATER(Items.VIAL_OF_WATER_227, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401),
|
||||
BOWL_OF_WATER(Items.BOWL_OF_WATER_1921, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
|
||||
BOWL_OF_HOT_WATER(Items.BOWL_OF_HOT_WATER_4456, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
|
||||
|
|
|
|||
|
|
@ -50,15 +50,15 @@ class EnchantedJewelleryListener : InteractionListener {
|
|||
jewellery.use(player, item, 0, isEquipped)
|
||||
return
|
||||
}
|
||||
openDialogue(player,EnchantedJewelleryDialogueFile(jewellery,item,isEquipped))
|
||||
openDialogue(player,EnchantedJewelleryDialogueFile(jewellery, item, true, isEquipped))
|
||||
}
|
||||
}
|
||||
|
||||
class EnchantedJewelleryDialogueFile(val jewellery: EnchantedJewellery, val item: Item, val isEquipped: Boolean) : DialogueFile() {
|
||||
class EnchantedJewelleryDialogueFile(val jewellery: EnchantedJewellery, val item: Item, val replace: Boolean, val isEquipped: Boolean) : DialogueFile() {
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
when (stage) {
|
||||
START_DIALOGUE -> {
|
||||
interpreter!!.sendOptions("Where would you like to go?", *jewellery.options)
|
||||
interpreter!!.sendOptions("Where would you like to teleport to?", *jewellery.options)
|
||||
stage++
|
||||
}
|
||||
1 -> end().also { jewellery.use(player!!, item, buttonID - 1, isEquipped) }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package content.global.handlers.item
|
||||
|
||||
import core.api.getAttribute
|
||||
import core.api.sendMessage
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class RingForgingOperateListener: InteractionListener {
|
||||
override fun defineListeners() {
|
||||
|
||||
on(Items.RING_OF_FORGING_2568, IntType.ITEM, "operate") { player, node ->
|
||||
|
||||
//Attribute set at SmeltingPulse.java
|
||||
val charges = getAttribute(player, "ringOfForgingCharges", 140)
|
||||
|
||||
sendMessage(player, "You can smelt $charges more iron ore before the ring disintegrates.")
|
||||
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.ServerConstants
|
||||
import core.api.*
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -244,8 +245,9 @@ class GoblinBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.GOBLIN_BOOK_10999, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.GOBLIN_BOOK_10999, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_26, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -126,8 +127,9 @@ class SecurityBookPlugin : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.SECURITY_BOOK_9003, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.SECURITY_BOOK_9003, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_2_27, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -128,8 +129,9 @@ class StrongholdNotes : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.STRONGHOLD_NOTES_9004, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.STRONGHOLD_NOTES_9004, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_2_27, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package content.global.handlers.item.toys;
|
||||
|
||||
import content.global.skill.construction.decoration.pohstorage.StorableFamily;
|
||||
import core.game.component.Component;
|
||||
import core.game.component.ComponentDefinition;
|
||||
import core.game.component.ComponentPlugin;
|
||||
|
|
@ -46,7 +47,7 @@ public class DiangoReclaimInterface extends ComponentPlugin {
|
|||
Item[] reclaimables = getEligibleItems(player);
|
||||
player.setAttribute("diango-reclaimables", reclaimables);
|
||||
|
||||
//filter out items the player already has in their bank, inventory, or equipped
|
||||
//filter out items the player already has in their bank, POH, inventory, or equipped
|
||||
|
||||
//only send items if there are some to send
|
||||
if(reclaimables.length > 0) {
|
||||
|
|
@ -90,7 +91,7 @@ public class DiangoReclaimInterface extends ComponentPlugin {
|
|||
|
||||
public static Item[] getEligibleItems (Player player) {
|
||||
return ITEMS.stream().filter(Objects::nonNull)
|
||||
.filter(item -> !player.getEquipment().containsItem(item) && !player.getInventory().containsItem(item) && !player.getBank().containsItem(item)
|
||||
.filter(item -> !player.getEquipment().containsItem(item) && !player.getInventory().containsItem(item) && !player.getBank().containsItem(item) && !player.getPOHStorageState().getContainer(StorableFamily.TOY_BOX).contains(item.getId())
|
||||
&& (item.getId() != 14654
|
||||
|| (!(inInventory(player, 14655, 1) || inEquipment(player, 14656, 1)) && player.getAttribute("sotr:purchased",false))
|
||||
))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package content.global.handlers.item.withitem
|
||||
|
||||
import core.api.addItem
|
||||
import core.api.animate
|
||||
import core.api.removeItem
|
||||
import core.game.interaction.InteractionListener
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class CalquatKeg : InteractionListener{
|
||||
override fun defineListeners() {
|
||||
onUseWith(ITEM, Items.CALQUAT_FRUIT_5980, Items.KNIFE_946) { player, _, _ ->
|
||||
if (removeItem(player, Items.CALQUAT_FRUIT_5980)){
|
||||
animate(player,2290)
|
||||
addItem(player, Items.CALQUAT_KEG_5769)
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package content.global.handlers.npc
|
||||
|
||||
import core.api.*
|
||||
import core.api.playGlobalAudio
|
||||
import core.api.sendGraphics
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import org.rs09.consts.NPCs
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.tools.RandomFunction
|
||||
import core.game.world.update.flag.context.Graphics
|
||||
import core.game.node.entity.combat.ImpactHandler.HitsplatType;
|
||||
|
||||
private val chinchompaIDs = intArrayOf(
|
||||
NPCs.CHINCHOMPA_5079,
|
||||
NPCs.CARNIVOROUS_CHINCHOMPA_5080
|
||||
)
|
||||
|
||||
/**
|
||||
* Minimal evasive approach to the Chinchompas exploding on death cause by combat and not by trapping.
|
||||
*/
|
||||
class ChinchompaDeathListener: NPCBehavior(*chinchompaIDs) {
|
||||
override fun onDeathFinished(self: NPC, killer: Entity) {
|
||||
|
||||
// If it 'died' from getting trapped an attribute is set of 'hunter'.
|
||||
if (!self.attributes.containsKey("hunter") && killer is Player) {
|
||||
val explosionLoc = self.location
|
||||
val validExplosionDmgTiles = explosionLoc.surroundingTiles
|
||||
|
||||
val chinExplosionGFX = Graphics(157, 96)
|
||||
sendGraphics(chinExplosionGFX, explosionLoc)
|
||||
playGlobalAudio(explosionLoc, 158)
|
||||
|
||||
if (validExplosionDmgTiles.contains(killer.location)) {
|
||||
//As weird this looks, it does allow to have a hit from 0 to 2
|
||||
val hitAmount = RandomFunction.random(0, 3)
|
||||
impact(killer, hitAmount, HitsplatType.NORMAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* At content.global.skill.hunter.TrapSetting
|
||||
* sets an attribute of 'hunter' during it's getCatchPulse for reward rolling, but it never cleans it up after.
|
||||
* These NPCs is a member of NPC class although exists a HunterNPC class, but it's mainly used for imps.
|
||||
* Logically you'd do this after death finishes, but it would interfere with the reward rolls and I fear it may affect other hunter NPCs.
|
||||
*/
|
||||
override fun onRespawn(self: NPC) {
|
||||
self.removeAttribute("hunter")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package content.global.handlers.npc
|
||||
|
||||
import content.data.consumables.Consumables
|
||||
import content.global.skill.cooking.fermenting.CalquatDecant
|
||||
import core.api.*
|
||||
import core.game.consumable.Potion
|
||||
import core.game.dialogue.DialogueFile
|
||||
|
|
@ -14,23 +15,39 @@ class DecantListener : InteractionListener {
|
|||
|
||||
companion object {
|
||||
val potions = Consumables.potions.toIntArray()
|
||||
val decantables = potions + CalquatDecant.allKegIds
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(IntType.NPC,"decant"){ player, node ->
|
||||
val (toRemove, toAdd) = decantContainer(player.inventory)
|
||||
for (item in toRemove)
|
||||
removeItem(player, item)
|
||||
for (item in toAdd)
|
||||
// Decant potions
|
||||
val (potionRemove, potionAdd) = decantContainer(player.inventory)
|
||||
for (item in potionRemove)
|
||||
if (!removeItem(player, item)) { return@on false }
|
||||
for (item in potionAdd)
|
||||
addItem(player, item.id, item.amount)
|
||||
|
||||
// Decant kegs
|
||||
val (kegRemove, kegAdd) = CalquatDecant.decantCalquat(player.inventory)
|
||||
for (item in kegRemove)
|
||||
if (!removeItem(player, item)) { return@on false }
|
||||
for (item in kegAdd)
|
||||
addItem(player, item.id, item.amount)
|
||||
|
||||
player.dialogueInterpreter.open(DecantingDialogue(),node.asNpc())
|
||||
return@on true
|
||||
}
|
||||
|
||||
onUseWith(IntType.ITEM, potions, *potions) { player, used, with ->
|
||||
onUseWith(IntType.ITEM, decantables, *decantables) { player, used, with ->
|
||||
if (used !is Item) return@onUseWith false
|
||||
if (with !is Item) return@onUseWith false
|
||||
|
||||
// Check if these are kegs - handle with CalquatDecant
|
||||
if (CalquatDecant.isKeg(used.id) || CalquatDecant.isKeg(with.id)) {
|
||||
return@onUseWith CalquatDecant.combineKegs(player, used, with)
|
||||
}
|
||||
|
||||
// Otherwise handle as potions
|
||||
// Verify these are both the same potion types
|
||||
val potionUsed = Consumables.getConsumableById(used.id)?.consumable as? Potion ?: return@onUseWith false
|
||||
val potionWith = Consumables.getConsumableById(with.id)?.consumable as? Potion ?: return@onUseWith false
|
||||
|
|
@ -83,6 +100,57 @@ class DecantListener : InteractionListener {
|
|||
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Decant keg into beer glass
|
||||
onUseWith(IntType.ITEM, Items.BEER_GLASS_1919, *CalquatDecant.allKegIds) { player, used, with ->
|
||||
if (used !is Item || with !is Item) return@onUseWith false
|
||||
|
||||
// Determine which is the glass and which is the keg
|
||||
val (glass, keg) = if (used.id == Items.BEER_GLASS_1919) {
|
||||
used to with
|
||||
} else {
|
||||
with to used
|
||||
}
|
||||
|
||||
return@onUseWith CalquatDecant.pourIntoGlass(player, glass, keg)
|
||||
}
|
||||
|
||||
// Barbarian decanting: use a 4-dose potion and an empty vial together to make two 2-dose potions
|
||||
onUseWith(IntType.ITEM, potions, Items.VIAL_229) { player, used, with ->
|
||||
// Check if player has started barbarian herblore training
|
||||
if (getAttribute(player, "/save:barbtraining:herblore", 0) == 0) {
|
||||
return@onUseWith false
|
||||
}
|
||||
|
||||
if (used !is Item) return@onUseWith false
|
||||
if (with !is Item) return@onUseWith false
|
||||
|
||||
// Determine which item is the potion and which is the vial
|
||||
val (potionItem) = if (used.id == Items.VIAL_229) {
|
||||
Pair(with, used)
|
||||
} else {
|
||||
Pair(used, with)
|
||||
}
|
||||
|
||||
// Check if the potion is 4 doses
|
||||
val potion = Consumables.getConsumableById(potionItem.id)?.consumable as? Potion ?: return@onUseWith false
|
||||
val dose = potion.getDose(potionItem)
|
||||
|
||||
if (dose != 4) {
|
||||
return@onUseWith false
|
||||
}
|
||||
|
||||
// Get the 2-dose version of this potion by index
|
||||
val twoDoseId = potion.ids[2]
|
||||
|
||||
// Replace ingredients with products
|
||||
replaceSlot(player, with.slot, Item(twoDoseId))
|
||||
replaceSlot(player, used.slot, Item(twoDoseId))
|
||||
|
||||
playAudio(player, 2401)
|
||||
|
||||
return@onUseWith true
|
||||
}
|
||||
}
|
||||
|
||||
internal class DecantingDialogue : DialogueFile(){
|
||||
|
|
|
|||
426
Server/src/main/content/global/handlers/scenery/TableListener.kt
Normal file
426
Server/src/main/content/global/handlers/scenery/TableListener.kt
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
package content.global.handlers.scenery
|
||||
|
||||
import content.global.skill.summoning.pet.Pets
|
||||
import core.api.*
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.QueueStrength
|
||||
import core.game.node.entity.combat.graves.GraveController
|
||||
import core.game.node.entity.player.info.LogType
|
||||
import core.game.node.entity.player.info.PlayerMonitor
|
||||
import core.game.node.entity.player.info.login.PlayerParser
|
||||
import core.game.node.item.GroundItem
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.config.ItemConfigParser
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.Scenery
|
||||
import org.rs09.consts.Sounds
|
||||
|
||||
/** To allow players to place items on tables. Copies DropListener for restrictions. */
|
||||
class TableListener : InteractionListener {
|
||||
companion object {
|
||||
private val DROP_COINS_SOUND = Sounds.EYEGLO_COIN_10
|
||||
private val DROP_ITEM_SOUND = Sounds.PUT_DOWN_2739
|
||||
}
|
||||
override fun defineListeners() {
|
||||
onUseAnyWith(
|
||||
IntType.SCENERY,
|
||||
Scenery.TABLE_10258,
|
||||
Scenery.TABLE_10414,
|
||||
Scenery.TABLE_10490,
|
||||
Scenery.TABLE_10509,
|
||||
Scenery.TABLE_10808,
|
||||
Scenery.TABLE_11202,
|
||||
Scenery.TABLE_11203,
|
||||
Scenery.TABLE_11500,
|
||||
Scenery.TABLE_11501,
|
||||
Scenery.TABLE_11502,
|
||||
Scenery.TABLE_11503,
|
||||
Scenery.TABLE_11504,
|
||||
Scenery.TABLE_11551,
|
||||
Scenery.TABLE_11619,
|
||||
Scenery.TABLE_11660,
|
||||
Scenery.TABLE_11682,
|
||||
Scenery.TABLE_11743,
|
||||
Scenery.TABLE_11748,
|
||||
Scenery.TABLE_11749,
|
||||
Scenery.TABLE_11849,
|
||||
Scenery.TABLE_11892,
|
||||
Scenery.TABLE_12106,
|
||||
Scenery.TABLE_12108,
|
||||
Scenery.TABLE_12386,
|
||||
Scenery.TABLE_12387,
|
||||
Scenery.TABLE_12388,
|
||||
Scenery.TABLE_12450,
|
||||
Scenery.TABLE_12541,
|
||||
Scenery.TABLE_12544,
|
||||
Scenery.TABLE_12734,
|
||||
Scenery.TABLE_12855,
|
||||
Scenery.TABLE_12884,
|
||||
Scenery.TABLE_12962,
|
||||
Scenery.TABLE_12971,
|
||||
Scenery.TABLE_12972,
|
||||
Scenery.TABLE_14044,
|
||||
Scenery.TABLE_14049,
|
||||
Scenery.TABLE_14163,
|
||||
Scenery.TABLE_14363,
|
||||
Scenery.TABLE_14762,
|
||||
Scenery.TABLE_14763,
|
||||
Scenery.TABLE_14920,
|
||||
Scenery.TABLE_15044,
|
||||
Scenery.TABLE_15092,
|
||||
Scenery.TABLE_15137,
|
||||
Scenery.TABLE_15680,
|
||||
Scenery.TABLE_15684,
|
||||
Scenery.TABLE_15719,
|
||||
Scenery.TABLE_15982,
|
||||
Scenery.TABLE_16169,
|
||||
Scenery.TABLE_16170,
|
||||
Scenery.TABLE_16562,
|
||||
Scenery.TABLE_16737,
|
||||
Scenery.TABLE_16738,
|
||||
Scenery.TABLE_16739,
|
||||
Scenery.TABLE_16740,
|
||||
Scenery.TABLE_16741,
|
||||
Scenery.TABLE_16742,
|
||||
Scenery.TABLE_16815,
|
||||
Scenery.TABLE_16827,
|
||||
Scenery.TABLE_16828,
|
||||
Scenery.TABLE_16874,
|
||||
Scenery.TABLE_16974,
|
||||
Scenery.TABLE_17099,
|
||||
Scenery.TABLE_17145,
|
||||
Scenery.TABLE_17235,
|
||||
Scenery.TABLE_17299,
|
||||
Scenery.TABLE_17348,
|
||||
Scenery.TABLE_17545,
|
||||
Scenery.TABLE_17546,
|
||||
Scenery.TABLE_17661,
|
||||
Scenery.TABLE_17662,
|
||||
Scenery.TABLE_17666,
|
||||
Scenery.TABLE_17762,
|
||||
Scenery.TABLE_18051,
|
||||
Scenery.TABLE_18059,
|
||||
Scenery.TABLE_18062,
|
||||
Scenery.TABLE_18314,
|
||||
Scenery.TABLE_18486,
|
||||
Scenery.TABLE_18902,
|
||||
Scenery.TABLE_1901,
|
||||
Scenery.TABLE_19158,
|
||||
Scenery.TABLE_19928,
|
||||
Scenery.TABLE_20058,
|
||||
Scenery.TABLE_20060,
|
||||
Scenery.TABLE_20351,
|
||||
Scenery.TABLE_20388,
|
||||
Scenery.TABLE_20996,
|
||||
Scenery.TABLE_21359,
|
||||
Scenery.TABLE_21360,
|
||||
Scenery.TABLE_21388,
|
||||
Scenery.TABLE_21389,
|
||||
Scenery.TABLE_21608,
|
||||
Scenery.TABLE_21609,
|
||||
Scenery.TABLE_21610,
|
||||
Scenery.TABLE_21793,
|
||||
Scenery.TABLE_22159,
|
||||
Scenery.TABLE_22160,
|
||||
Scenery.TABLE_22431,
|
||||
Scenery.TABLE_22672,
|
||||
Scenery.TABLE_22677,
|
||||
Scenery.TABLE_22678,
|
||||
Scenery.TABLE_22690,
|
||||
Scenery.TABLE_22693,
|
||||
Scenery.TABLE_22694,
|
||||
Scenery.TABLE_22705,
|
||||
Scenery.TABLE_22830,
|
||||
Scenery.TABLE_22831,
|
||||
Scenery.TABLE_22832,
|
||||
Scenery.TABLE_22844,
|
||||
Scenery.TABLE_22845,
|
||||
Scenery.TABLE_22846,
|
||||
Scenery.TABLE_22847,
|
||||
Scenery.TABLE_22848,
|
||||
Scenery.TABLE_22849,
|
||||
Scenery.TABLE_23177,
|
||||
Scenery.TABLE_23942,
|
||||
Scenery.TABLE_24011,
|
||||
Scenery.TABLE_24012,
|
||||
Scenery.TABLE_24013,
|
||||
Scenery.TABLE_24014,
|
||||
Scenery.TABLE_24028,
|
||||
Scenery.TABLE_24076,
|
||||
Scenery.TABLE_24084,
|
||||
Scenery.TABLE_24088,
|
||||
Scenery.TABLE_24104,
|
||||
Scenery.TABLE_24301,
|
||||
Scenery.TABLE_24302,
|
||||
Scenery.TABLE_24303,
|
||||
Scenery.TABLE_24307,
|
||||
Scenery.TABLE_24308,
|
||||
Scenery.TABLE_24309,
|
||||
Scenery.TABLE_24320,
|
||||
Scenery.TABLE_24335,
|
||||
Scenery.TABLE_24336,
|
||||
Scenery.TABLE_24667,
|
||||
Scenery.TABLE_24684,
|
||||
Scenery.TABLE_24900,
|
||||
Scenery.TABLE_24922,
|
||||
Scenery.TABLE_24927,
|
||||
Scenery.TABLE_25047,
|
||||
Scenery.TABLE_25603,
|
||||
Scenery.TABLE_25615,
|
||||
Scenery.TABLE_25733,
|
||||
Scenery.TABLE_25734,
|
||||
Scenery.TABLE_25778,
|
||||
Scenery.TABLE_25928,
|
||||
Scenery.TABLE_25930,
|
||||
Scenery.TABLE_25931,
|
||||
Scenery.TABLE_26020,
|
||||
Scenery.TABLE_26074,
|
||||
Scenery.TABLE_26142,
|
||||
Scenery.TABLE_2650,
|
||||
Scenery.TABLE_26783,
|
||||
Scenery.TABLE_26807,
|
||||
Scenery.TABLE_27014,
|
||||
Scenery.TABLE_27052,
|
||||
Scenery.TABLE_27056,
|
||||
Scenery.TABLE_27083,
|
||||
Scenery.TABLE_27245,
|
||||
Scenery.TABLE_27850,
|
||||
Scenery.TABLE_28199,
|
||||
Scenery.TABLE_28262,
|
||||
Scenery.TABLE_28508,
|
||||
Scenery.TABLE_28621,
|
||||
Scenery.TABLE_28628,
|
||||
Scenery.TABLE_28629,
|
||||
Scenery.TABLE_28660,
|
||||
Scenery.TABLE_2906,
|
||||
Scenery.TABLE_29131,
|
||||
Scenery.TABLE_2940,
|
||||
Scenery.TABLE_29467,
|
||||
Scenery.TABLE_29468,
|
||||
Scenery.TABLE_30074,
|
||||
Scenery.TABLE_30075,
|
||||
Scenery.TABLE_30076,
|
||||
Scenery.TABLE_30077,
|
||||
Scenery.TABLE_30078,
|
||||
Scenery.TABLE_30079,
|
||||
Scenery.TABLE_30080,
|
||||
Scenery.TABLE_30081,
|
||||
Scenery.TABLE_30222,
|
||||
Scenery.TABLE_30734,
|
||||
Scenery.TABLE_30827,
|
||||
Scenery.TABLE_30828,
|
||||
Scenery.TABLE_30830,
|
||||
Scenery.TABLE_30939,
|
||||
Scenery.TABLE_32121,
|
||||
Scenery.TABLE_33052,
|
||||
Scenery.TABLE_33054,
|
||||
Scenery.TABLE_33129,
|
||||
Scenery.TABLE_33159,
|
||||
Scenery.TABLE_33201,
|
||||
Scenery.TABLE_33202,
|
||||
Scenery.TABLE_33233,
|
||||
Scenery.TABLE_34204,
|
||||
Scenery.TABLE_34429,
|
||||
Scenery.TABLE_34431,
|
||||
Scenery.TABLE_34469,
|
||||
Scenery.TABLE_34470,
|
||||
Scenery.TABLE_34473,
|
||||
Scenery.TABLE_34474,
|
||||
Scenery.TABLE_34518,
|
||||
Scenery.TABLE_34519,
|
||||
Scenery.TABLE_34520,
|
||||
Scenery.TABLE_34521,
|
||||
Scenery.TABLE_34799,
|
||||
Scenery.TABLE_34927,
|
||||
Scenery.TABLE_34968,
|
||||
Scenery.TABLE_36255,
|
||||
Scenery.TABLE_36350,
|
||||
Scenery.TABLE_36366,
|
||||
Scenery.TABLE_36573,
|
||||
Scenery.TABLE_36574,
|
||||
Scenery.TABLE_36575,
|
||||
Scenery.TABLE_36576,
|
||||
Scenery.TABLE_36577,
|
||||
Scenery.TABLE_36578,
|
||||
Scenery.TABLE_36579,
|
||||
Scenery.TABLE_36580,
|
||||
Scenery.TABLE_36581,
|
||||
Scenery.TABLE_36582,
|
||||
Scenery.TABLE_36583,
|
||||
Scenery.TABLE_36584,
|
||||
Scenery.TABLE_36585,
|
||||
Scenery.TABLE_36586,
|
||||
Scenery.TABLE_36727,
|
||||
Scenery.TABLE_36728,
|
||||
Scenery.TABLE_36730,
|
||||
Scenery.TABLE_36731,
|
||||
Scenery.TABLE_36820,
|
||||
Scenery.TABLE_36821,
|
||||
Scenery.TABLE_36910,
|
||||
Scenery.TABLE_37160,
|
||||
Scenery.TABLE_37161,
|
||||
Scenery.TABLE_37162,
|
||||
Scenery.TABLE_37163,
|
||||
Scenery.TABLE_37164,
|
||||
Scenery.TABLE_37165,
|
||||
Scenery.TABLE_37166,
|
||||
Scenery.TABLE_37167,
|
||||
Scenery.TABLE_37350,
|
||||
Scenery.TABLE_37419,
|
||||
Scenery.TABLE_37815,
|
||||
Scenery.TABLE_38020,
|
||||
Scenery.TABLE_38021,
|
||||
Scenery.TABLE_38102,
|
||||
Scenery.TABLE_38316,
|
||||
Scenery.TABLE_38317,
|
||||
Scenery.TABLE_38318,
|
||||
Scenery.TABLE_38319,
|
||||
Scenery.TABLE_38320,
|
||||
Scenery.TABLE_39167,
|
||||
Scenery.TABLE_39168,
|
||||
Scenery.TABLE_40030,
|
||||
Scenery.TABLE_40031,
|
||||
Scenery.TABLE_40818,
|
||||
Scenery.TABLE_40819,
|
||||
Scenery.TABLE_40820,
|
||||
Scenery.TABLE_41105,
|
||||
Scenery.TABLE_41106,
|
||||
Scenery.TABLE_41107,
|
||||
Scenery.TABLE_41196,
|
||||
Scenery.TABLE_41289,
|
||||
Scenery.TABLE_41462,
|
||||
Scenery.TABLE_41469,
|
||||
Scenery.TABLE_4272,
|
||||
Scenery.TABLE_4372,
|
||||
Scenery.TABLE_4458,
|
||||
Scenery.TABLE_4459,
|
||||
Scenery.TABLE_4460,
|
||||
Scenery.TABLE_4461,
|
||||
Scenery.TABLE_4462,
|
||||
Scenery.TABLE_4463,
|
||||
Scenery.TABLE_4464,
|
||||
Scenery.TABLE_4651,
|
||||
Scenery.TABLE_4652,
|
||||
Scenery.TABLE_4653,
|
||||
Scenery.TABLE_5349,
|
||||
Scenery.TABLE_5879,
|
||||
Scenery.TABLE_593,
|
||||
Scenery.TABLE_594,
|
||||
Scenery.TABLE_595,
|
||||
Scenery.TABLE_596,
|
||||
Scenery.TABLE_597,
|
||||
Scenery.TABLE_598,
|
||||
Scenery.TABLE_599,
|
||||
Scenery.TABLE_600,
|
||||
Scenery.TABLE_601,
|
||||
Scenery.TABLE_602,
|
||||
Scenery.TABLE_603,
|
||||
Scenery.TABLE_604,
|
||||
Scenery.TABLE_605,
|
||||
Scenery.TABLE_606,
|
||||
Scenery.TABLE_607,
|
||||
Scenery.TABLE_6075,
|
||||
Scenery.TABLE_613,
|
||||
Scenery.TABLE_614,
|
||||
Scenery.TABLE_616,
|
||||
Scenery.TABLE_6196,
|
||||
Scenery.TABLE_6197,
|
||||
Scenery.TABLE_6198,
|
||||
Scenery.TABLE_6199,
|
||||
Scenery.TABLE_620,
|
||||
Scenery.TABLE_6200,
|
||||
Scenery.TABLE_6201,
|
||||
Scenery.TABLE_621,
|
||||
Scenery.TABLE_6211,
|
||||
Scenery.TABLE_622,
|
||||
Scenery.TABLE_623,
|
||||
Scenery.TABLE_6231,
|
||||
Scenery.TABLE_624,
|
||||
Scenery.TABLE_6246,
|
||||
Scenery.TABLE_7331,
|
||||
Scenery.TABLE_8700,
|
||||
Scenery.TABLE_8701,
|
||||
Scenery.TABLE_8751,
|
||||
Scenery.TABLE_8768,
|
||||
Scenery.TABLE_8769,
|
||||
Scenery.TABLE_9082,
|
||||
Scenery.TABLE_9430,
|
||||
Scenery.TABLE_9431,
|
||||
Scenery.TABLE_9432,
|
||||
Scenery.TABLE_9433,
|
||||
Scenery.TABLE_9434,
|
||||
Scenery.TABLE_9614,
|
||||
Scenery.TABLE_9685,
|
||||
Scenery.TABLE_9702
|
||||
) { player, used, with ->
|
||||
|
||||
used as Item
|
||||
with as core.game.node.scenery.Scenery
|
||||
|
||||
// Similar to DropListener.handleDropAction to keep it consistent
|
||||
if (Pets.forId(used.id) != null) {
|
||||
player.familiarManager.summon(used, true, true)
|
||||
return@onUseAnyWith true
|
||||
}
|
||||
if (player.locks.equipmentLock != null) {
|
||||
return@onUseAnyWith false
|
||||
}
|
||||
|
||||
// determine if item can be dropped
|
||||
var canDrop = false;
|
||||
for (option in used.getDefinition().getOptions()) {
|
||||
if (option != null && (option.contains("drop"))) {
|
||||
canDrop = true
|
||||
}
|
||||
}
|
||||
if (!canDrop) {
|
||||
sendMessage(player, "You cannot place this item on the table!")
|
||||
return@onUseAnyWith false
|
||||
}
|
||||
|
||||
// Calculate nearest
|
||||
val minX = with.location.x
|
||||
val maxX = with.location.x + with.getSizeX() - 1
|
||||
val minY = with.location.y
|
||||
val maxY = with.location.y + with.getSizeY() - 1
|
||||
val nearestX = when {
|
||||
player.location.x < minX -> minX
|
||||
player.location.x > maxX -> maxX
|
||||
else -> player.location.x
|
||||
}
|
||||
val nearestY = when {
|
||||
player.location.y < minY -> minY
|
||||
player.location.y > maxY -> maxY
|
||||
else -> player.location.y
|
||||
}
|
||||
|
||||
// Similar to DropListener.handleDropAction to keep it consistent
|
||||
closeAllInterfaces(player)
|
||||
player.animate(Animation(537))
|
||||
queueScript(player, strength = QueueStrength.SOFT) { //do this as a script to allow dropping multiple items in the same tick (authentic)
|
||||
// It's possible for state to change between queueing the script and executing it at the end of the tick (https://forum.2009scape.org/viewtopic.php?f=8&t=1195-lost-bandos-chestplate-whilst-making-iron-titans&p=5292). So, sanity check:
|
||||
val current = player.inventory.get(used.slot)
|
||||
if (current == null || current !== used) {
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
if (player.inventory.replace(null, used.slot) !== used) {
|
||||
PlayerMonitor.log(player, LogType.DUPE_ALERT, "Potential exploit attempt when player ${player.name} tried to place on table ${used.amount}x ${used.id}. The item has been lost and will need to be refunded to the player, but how did they get this to happen?")
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
val droppedItem = used.dropItem
|
||||
if (droppedItem.id == Items.COINS_995) playAudio(player, DROP_COINS_SOUND) else playAudio(player, DROP_ITEM_SOUND)
|
||||
GroundItemManager.create(droppedItem, Location(nearestX, nearestY), player)
|
||||
|
||||
setAttribute(player, "droppedItem:${droppedItem.id}", getWorldTicks() + 2)
|
||||
PlayerParser.save(player)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
|
||||
return@onUseAnyWith true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -90,8 +90,6 @@ public final class BuildingUtils {
|
|||
|
||||
for (int menuIndex = 0; menuIndex < 7; menuIndex++) {
|
||||
int itemsStringOffset = 97 + (menuIndex * 5);
|
||||
|
||||
//97 +
|
||||
if (menuIndex >= hotspot.getDecorations().length || (hotspot.getDecorations()[menuIndex] != null && hotspot.getDecorations()[menuIndex].isInvisibleNode())) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
player.getPacketDispatch().sendString("", 396, itemsStringOffset + j);
|
||||
|
|
@ -104,43 +102,24 @@ public final class BuildingUtils {
|
|||
Decoration decoration = hotspot.getDecorations()[menuIndex];
|
||||
items[BUILD_INDEXES[menuIndex]] = new Item(decoration.getInterfaceItem());
|
||||
player.getPacketDispatch().sendString(ItemDefinition.forId(decoration.getInterfaceItem()).getName(), 396, itemsStringOffset);
|
||||
boolean hasRequirements = player.getSkills().getLevel(Skills.CONSTRUCTION) >= decoration.getLevel();
|
||||
boolean hasRequirements = player.getSkills().getLevel(Skills.CONSTRUCTION) >= decoration.getLevel(); //nails are checked in buildDecoration
|
||||
Item[] reqItems = decoration.getItems();
|
||||
String[] reqsText = decoration.getReqsText();
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (j >= decoration.getItems().length) {
|
||||
if (j == decoration.getItems().length && decoration.getNailAmount() > 0) {
|
||||
player.getPacketDispatch().sendString("Nails: " + decoration.getNailAmount(), 396, (itemsStringOffset + 1) + j);
|
||||
} else {
|
||||
player.getPacketDispatch().sendString("", 396, (itemsStringOffset + 1) + j);
|
||||
}
|
||||
} else {
|
||||
Item item = decoration.getItems()[j];
|
||||
if (!player.getInventory().containsItem(item)) {
|
||||
hasRequirements = false;
|
||||
}
|
||||
String s = item.getName() + ": " + item.getAmount();
|
||||
/*if (j > 1 && (decoration == Decoration.RUNE_CASE1 || decoration == Decoration.RUNE_CASE2)) {
|
||||
if (j == 3) {
|
||||
offset--;
|
||||
item = decoration.getItems()[++j];
|
||||
s = item.getName() + ": " + item.getAmount();
|
||||
}
|
||||
item = decoration.getItems()[j + 1];
|
||||
s += ", " + item.getName() + ": " + item.getAmount();
|
||||
player.getPacketDispatch().sendString(s, 396, 15 + offset + j);
|
||||
continue;
|
||||
}*/
|
||||
player.getPacketDispatch().sendString(s, 396, (itemsStringOffset + 1) + j);
|
||||
if (j < reqItems.length && !player.getInventory().containsItem(reqItems[j])) {
|
||||
hasRequirements = false;
|
||||
}
|
||||
player.getPacketDispatch().sendString(reqsText[j], 396, (itemsStringOffset + 1) + j);
|
||||
}
|
||||
if (hasRequirements) {
|
||||
c261Value += (1 << (menuIndex + 1));
|
||||
}
|
||||
setVarp(player, 1485 + menuIndex, hasRequirements || player.isStaff() ? 1 : 0);
|
||||
setVarp(player, 1485 + menuIndex, hasRequirements || player.isStaff() ? 1 : 0);
|
||||
player.getPacketDispatch().sendString("Level " + decoration.getLevel(), 396, 140 + menuIndex);
|
||||
//player.getPacketDispatch().sendItemZoomOnInterface(items[i].protocol(), 50000, 396, 49 + i);
|
||||
}
|
||||
|
||||
setVarp(player, 261, c261Value);
|
||||
setVarp(player, 261, c261Value);
|
||||
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 396, 132, 8, items, false));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package content.global.skill.construction
|
||||
|
||||
import core.api.sendMessage
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.dialogue.DialogueInterpreter
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
|
|
@ -52,7 +53,7 @@ class ConstructionGuideBook : InteractionListener {
|
|||
|
||||
override fun defineListeners() {
|
||||
// There is supposedly a book here.
|
||||
on(Items.CONSTRUCTION_GUIDE_8463, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.CONSTRUCTION_GUIDE_8463, IntType.ITEM, "read") { player, node ->
|
||||
if (settings!!.isDevMode && settings!!.isBeta) {
|
||||
for (item in RESOURCES) {
|
||||
if (!player.inventory.contains(item.id, item.amount)) {
|
||||
|
|
@ -60,6 +61,7 @@ class ConstructionGuideBook : InteractionListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
storeBookInHouse(player, node)
|
||||
sendMessage(player, "Upon reading the book you discover you're supposed to use these resources to test out construction. Report all bugs on the forums.")
|
||||
return@on true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ public enum Decoration {
|
|||
* Quest hall decorations.
|
||||
*/
|
||||
ANTIDRAGON_SHIELD(13522, 8282, 47, 280, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ANTI_DRAGON_SHIELD_1540) }, new Item[] { new Item(Items.ANTI_DRAGON_SHIELD_1540) }),
|
||||
AMULET_OF_GLORY (13523, 8283, 47, 290, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.AMULET_OF_GLORY_1704) }, new Item[] { new Item(Items.AMULET_OF_GLORY_1704) }),
|
||||
AMULET_OF_GLORY (13523, 8283, 47, 290, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.AMULET_OF_GLORY_1704) }, new Item[] { new Item(Items.AMULET_OF_GLORY_1704) }, new String[] { "Teak plank: 3", "Amulet of Glory", "(uncharged): 1" }),
|
||||
CAPE_OF_LEGENDS (13524, 8284, 47, 300, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.CAPE_OF_LEGENDS_1052) }, new Item[] { new Item(Items.CAPE_OF_LEGENDS_1052) }),
|
||||
KING_ARTHUR (13510, 8285, 35, 211, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ARTHUR_PORTRAIT_7995) }),
|
||||
ELENA (13511, 8286, 35, 211, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.ELENA_PORTRAIT_7996) }),
|
||||
|
|
@ -727,23 +727,42 @@ public enum Decoration {
|
|||
private final int[] objectIds;
|
||||
|
||||
/**
|
||||
* If this node should be invisible to user build options
|
||||
* If this node should be invisible to user build options.
|
||||
*/
|
||||
private boolean invisibleNode;
|
||||
|
||||
/**
|
||||
* Constructs a new object, no items, no tools, no refund items.
|
||||
* The requirements text to show for this node.
|
||||
*/
|
||||
private String[] reqsText = {"", "", "", ""};
|
||||
|
||||
private String[] generateDefaultReqsText(Item[] items, int nailAmount) {
|
||||
String[] ret = {"", "", "", ""};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (i >= items.length) {
|
||||
if (i == items.length && nailAmount > 0) {
|
||||
ret[i] = "Nails: " + nailAmount;
|
||||
}
|
||||
} else {
|
||||
ret[i] = items[i].getName() + ": " + items[i].getAmount();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object, default items, default tools, default refund items, default requirements text.
|
||||
* @param objectId The object id.
|
||||
* @param interfaceItem The item id for the building interface.
|
||||
* @param level The level required.
|
||||
* @param experience The experience gained.
|
||||
*/
|
||||
Decoration(int objectId, int interfaceItem, int level, int experience) {
|
||||
this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, new Item[] {}, new Item[] {});
|
||||
this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, new Item[] {}, new Item[] {}, new String[] {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object, no tools, no refund items.
|
||||
* Constructs a new object, default tools, default refund items, default requirements text.
|
||||
* @param objectId The object id.
|
||||
* @param interfaceItem The item id for the building interface.
|
||||
* @param level The level required.
|
||||
|
|
@ -751,11 +770,12 @@ public enum Decoration {
|
|||
* @param items The items required.
|
||||
*/
|
||||
Decoration(int objectId, int interfaceItem, int level, int experience, Item[] items) {
|
||||
this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, new Item[] {});
|
||||
this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, new Item[] {}, new String[] {});
|
||||
this.reqsText = generateDefaultReqsText(items, getNailAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object, no refund items.
|
||||
* Constructs a new object, default refund items, default requirements text.
|
||||
* @param objectId The object id.
|
||||
* @param interfaceItem The item id for the building interface.
|
||||
* @param level The level required.
|
||||
|
|
@ -764,7 +784,8 @@ public enum Decoration {
|
|||
* @param items The items required.
|
||||
*/
|
||||
Decoration(int objectId, int interfaceItem, int level, int experience, int[] tools, Item[] items) {
|
||||
this(objectId, interfaceItem, level, experience, tools, items, new Item[] {});
|
||||
this(objectId, interfaceItem, level, experience, tools, items, new Item[] {}, new String[] {});
|
||||
this.reqsText = generateDefaultReqsText(items, getNailAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -777,7 +798,21 @@ public enum Decoration {
|
|||
* @param refundItems The items to be refunded when the item is removed.
|
||||
*/
|
||||
Decoration(int objectId, int interfaceItem, int level, int experience, Item[] items, Item[] refundItems) {
|
||||
this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, refundItems);
|
||||
this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, refundItems, new String[] {});
|
||||
this.reqsText = generateDefaultReqsText(items, getNailAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object, default tools.
|
||||
* @param objectId The object id.
|
||||
* @param interfaceItem The item id for the building interface.
|
||||
* @param level The level required.
|
||||
* @param experience The experience gained.
|
||||
* @param items The items required.
|
||||
* @param refundItems The items to be refunded when the item is removed.
|
||||
*/
|
||||
Decoration(int objectId, int interfaceItem, int level, int experience, Item[] items, Item[] refundItems, String[] reqsText) {
|
||||
this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, refundItems, reqsText);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -789,8 +824,9 @@ public enum Decoration {
|
|||
* @param tools The tools needed.
|
||||
* @param items The items required.
|
||||
* @param refundItems The items to be refunded when the item is removed.
|
||||
* @param reqsText The requirements text to be shown in the interface.
|
||||
*/
|
||||
Decoration(int objectId, int interfaceItem, int level, int experience, int[] tools, Item[] items, Item[] refundItems) {
|
||||
Decoration(int objectId, int interfaceItem, int level, int experience, int[] tools, Item[] items, Item[] refundItems, String[] reqsText) {
|
||||
this.objectId = objectId;
|
||||
this.objectIds = null;
|
||||
this.interfaceItem = interfaceItem;
|
||||
|
|
@ -799,6 +835,9 @@ public enum Decoration {
|
|||
this.tools = tools;
|
||||
this.items = items;
|
||||
this.refundItems = refundItems;
|
||||
if (reqsText.length > 0) {
|
||||
System.arraycopy(reqsText, 0, this.reqsText, 0, reqsText.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -822,18 +861,6 @@ public enum Decoration {
|
|||
Decoration(int[] objectIds, int interfaceItem, int level, int experience, Item[] items) {
|
||||
this(objectIds, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, new Item[] {});
|
||||
}
|
||||
/**
|
||||
* Constructs a new object no refund items.
|
||||
* @param objectIds The object id.
|
||||
* @param interfaceItem The item id for the building interface.
|
||||
* @param level The level required.
|
||||
* @param experience The experience gained.
|
||||
* @param tools The tools needed.
|
||||
* @param items The items required.
|
||||
*/
|
||||
Decoration(int[] objectIds, int interfaceItem, int level, int experience, int[] tools, Item[] items) {
|
||||
this(objectIds, interfaceItem, level, experience, tools, items, new Item[] {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object.
|
||||
|
|
@ -1000,4 +1027,12 @@ public enum Decoration {
|
|||
public boolean isInvisibleNode() {
|
||||
return invisibleNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the requirements text.
|
||||
* @return The requirements text.
|
||||
*/
|
||||
public String[] getReqsText() {
|
||||
return reqsText;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package content.global.skill.construction;
|
|||
|
||||
|
||||
import core.api.Container;
|
||||
import core.game.world.map.Location;
|
||||
import org.rs09.consts.Items;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.player.Player;
|
||||
|
|
@ -93,6 +94,28 @@ public final class HouseZone extends MapZone {
|
|||
public boolean leave(Entity e, boolean logout) {
|
||||
if (e instanceof Player) {
|
||||
Player p = (Player) e;
|
||||
|
||||
// if the player is moving between the dungeon and main house, don't trigger the leave sequence
|
||||
if (!logout) {
|
||||
// current loc
|
||||
Location dest = p.getProperties().getTeleportLocation();
|
||||
int currentRegionId = p.getLocation().getRegionId();
|
||||
int houseId = house.getHouseRegion().getId();
|
||||
int dungeonId = (house.getDungeonRegion() != null) ? house.getDungeonRegion().getId() : -1;
|
||||
|
||||
// check if still in house
|
||||
boolean currentlyInHouse = (currentRegionId == houseId || currentRegionId == dungeonId);
|
||||
|
||||
// check if moving between house regions
|
||||
int destRegionId = (dest != null) ? dest.getRegionId() : -1;
|
||||
boolean movingToHouse = (destRegionId == houseId || destRegionId == dungeonId);
|
||||
|
||||
// if yes, return true and stop the leave sequence
|
||||
if (currentlyInHouse || movingToHouse) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
remove_items(p);
|
||||
// The below tears down the house if the owner was the one who left
|
||||
if (house == p.getHouseManager()) {
|
||||
|
|
@ -105,11 +128,11 @@ public final class HouseZone extends MapZone {
|
|||
Region dr = dungRemove != -1 ? RegionManager.forId(dungRemove) : null;
|
||||
RegionManager.removeRegion(toRemove);
|
||||
unregisterRegion(toRemove);
|
||||
r.setActive(false);
|
||||
r.flagInactive();
|
||||
if (dungRemove != -1) {
|
||||
RegionManager.removeRegion(dungRemove);
|
||||
unregisterRegion(dungRemove);
|
||||
dr.setActive(false);
|
||||
dr.flagInactive();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@ package content.global.skill.construction;
|
|||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The styles of houses.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public enum HousingStyle {
|
||||
|
||||
// open door ids are doorId + 1
|
||||
|
||||
BASIC_WOOD (1, 5000, 7503, 0, 13100, 13101, 13098, Decoration.BASIC_WOOD_WINDOW),
|
||||
BASIC_STONE (10, 5000, 7503, 1, 13094, 13096, 1902, Decoration.BASIC_STONE_WINDOW),
|
||||
|
|
@ -16,6 +20,28 @@ public enum HousingStyle {
|
|||
FREMENNIK_STYLE_WOOD(30, 10000, 7503, 3, 13109, 13107, 13111, Decoration.FREMENNIK_WINDOW),
|
||||
TROPICAL_WOOD (40, 15000, 7759, 0, 13016, 13015, 13011, Decoration.TROPICAL_WOOD_WINDOW),
|
||||
FANCY_STONE (50, 25000, 7759, 1, 13119, 13118, 13116, Decoration.FANCY_STONE_WINDOW);
|
||||
|
||||
/**
|
||||
* Array of all Dungeon Wall IDs.
|
||||
* From Region 7503, Location.create(1898, 5084, 0)
|
||||
*/
|
||||
private static final int[] DUNGEON_WALL_IDS = {
|
||||
13019, 13020, 13021, 13022, 13023, 13024, 13025, 13026, 13027, 13028,
|
||||
13029, 13030, 13031, 13032, 13033, 13034, 13035, 13036, 13037, 13046,
|
||||
13048, 13049, 13050, 13051, 13055, 13056, 13058, 13059, 13060, 13061,
|
||||
13062, 13063, 13065, 13066, 13067, 13068, 13069, 13070, 13072, 13073,
|
||||
13074, 13075, 13076, 13077, 13079, 13080, 13081, 13082, 13083, 13084,
|
||||
13086, 13087, 13088, 13089
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the provided ID is a dungeon wall.
|
||||
* @param id The object ID.
|
||||
* @return {@code true} if it's a dungeon wall.
|
||||
*/
|
||||
public static boolean isDungeonWall(int id) {
|
||||
return Arrays.binarySearch(DUNGEON_WALL_IDS, id) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The level required.
|
||||
|
|
|
|||
|
|
@ -176,9 +176,10 @@ public final class Room {
|
|||
for (int i = 0; i < BuildRegionChunk.ARRAY_SIZE; i++) {
|
||||
Scenery object = chunk.get(x, y, i);
|
||||
if (object != null) {
|
||||
int id = object.getId();
|
||||
boolean isBuilt = object instanceof Constructed;
|
||||
boolean isWall = object.getId() == 13065 || object.getId() == house.getStyle().getWallId();
|
||||
boolean isDoor = object.getId() == house.getStyle().getDoorId() || object.getId() == house.getStyle().getSecondDoorId();
|
||||
boolean isWall = HousingStyle.isDungeonWall(id) || id == house.getStyle().getWallId();
|
||||
boolean isDoor = id == house.getStyle().getDoorId() || id == house.getStyle().getSecondDoorId();
|
||||
if (!isBuilt && !isWall && !isDoor) {
|
||||
SceneryBuilder.remove(object);
|
||||
chunk.remove(object);
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
package content.global.skill.construction.decoration.costume
|
||||
|
||||
import core.cache.def.impl.SceneryDefinition
|
||||
import core.game.interaction.OptionHandler
|
||||
import content.global.handlers.item.toys.DiangoReclaimInterface
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.plugin.Plugin
|
||||
|
||||
/**
|
||||
* Handles the Toy Box POH
|
||||
* ToyBoxPlugin.java
|
||||
* @author Lee
|
||||
* @date 10/2/2017
|
||||
*/
|
||||
@Initializable
|
||||
class ToyBoxPlugin : OptionHandler() {
|
||||
@Throws(Throwable::class)
|
||||
override fun newInstance(arg: Any?): Plugin<Any?> {
|
||||
SceneryDefinition.forId(18802).handlers["option:open"] = this
|
||||
return this
|
||||
}
|
||||
|
||||
override fun handle(player: Player, node: Node, option: String): Boolean {
|
||||
DiangoReclaimInterface.open(player)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package content.global.skill.construction.decoration.pohstorage;
|
||||
|
||||
import core.game.container.Container;
|
||||
import core.game.container.ContainerType;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.item.Item;
|
||||
|
||||
/**
|
||||
* A proxy Container used to indicate an item was found in POH storage.
|
||||
* This is not a real container, just a dummy so that hasAnItem() can be easily extended to check POH storage
|
||||
*/
|
||||
public class POHStorageProxyContainer extends Container {
|
||||
|
||||
public POHStorageProxyContainer(Player player) {
|
||||
super(1, ContainerType.DEFAULT);
|
||||
}
|
||||
|
||||
// Prevents external code from trying to fetch items from this container because it doesn't actually store anything
|
||||
@Override
|
||||
public Item get(int slot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "POHStorageProxyContainer";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,444 @@
|
|||
package content.global.skill.construction.decoration.pohstorage
|
||||
|
||||
import content.data.Quests
|
||||
import core.api.getItemName
|
||||
import core.api.isQuestComplete
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.GameWorld
|
||||
import org.rs09.consts.Items
|
||||
|
||||
/**
|
||||
* Represents a database of all the storable items in the POH Costume Room and Bookcase across all box types.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The below function should be copied into each book's listener to store the book in the POH. It is part of ContentAPI.
|
||||
* import core.api.*
|
||||
* storeBookInHouse(player, node)
|
||||
*
|
||||
* Example: src/main/content/region/kandarin/quest/observatoryquest/AstronomyBook.kt
|
||||
*
|
||||
* todo: Periodically, this storables list should be audited as new books are added to the game. Make sure the new books are going in the bookcase.
|
||||
*/
|
||||
|
||||
// todo: Right now there is no logic to check:
|
||||
// 1. for complete or incomplete sets
|
||||
// 2. for single instances of an item
|
||||
// This allows things like storing multiple god capes, a Rune (g) set with both platelegs and plateskirt,
|
||||
// an elegant shirt/pants and blouse/skirt at once, and all 3 sets of castle wars armour at once. I don't
|
||||
// think any of this is game-breaking, so have chosen to omit this logic for now.
|
||||
|
||||
enum class Storable(
|
||||
val displayId: Int, // display ID is the item that is shown in the UI
|
||||
val takeIds: IntArray, // take IDs is the item(s) that are taken from the player's inventory
|
||||
val type: StorableFamily, // the family (mapped to container) that the items get stored in
|
||||
val setName: String = getItemName(displayId), // the set/book name for each item. defaults to display item
|
||||
|
||||
// special properties just for the books
|
||||
val bitIndex: Int = -1, // This index is used to save the big list of books the player has read, instead of having an individual attribute per book.
|
||||
// todo make the quest requirement check part of a separate object against this list of books. only a subset of books are applicable.
|
||||
val questReq: Quests? = null // Once read, books should only show up in the POH after completing their attached quest. https://runescape.wiki/w/Bookcase?oldid=1650176
|
||||
) {
|
||||
// Books - https://runescape.wiki/w/List_of_texts_and_tomes?oldid=848552
|
||||
AbyssalBook(Items.ABYSSAL_BOOK_5520, intArrayOf(Items.ABYSSAL_BOOK_5520), type = StorableFamily.BOOKCASE, "Abyssal Research Notes", bitIndex = 0), // abyss miniquest - IMPLEMENTED
|
||||
AncientBook(Items.ANCIENT_BOOK_7633, intArrayOf(Items.ANCIENT_BOOK_7633), type = StorableFamily.BOOKCASE, "The Sleeping Seven", bitIndex = 1, Quests.HORROR_FROM_THE_DEEP), // horror from the deep quest
|
||||
AncientDiary(Items.DIARY_3846, intArrayOf(Items.DIARY_3846), type = StorableFamily.BOOKCASE, "Ancient Diary", bitIndex = 2, Quests.HORROR_FROM_THE_DEEP), // horror from the deep quest
|
||||
ArenaBook(Items.ARENA_BOOK_6891, intArrayOf(Items.ARENA_BOOK_6891), type = StorableFamily.BOOKCASE, "Magic Training Arena Lore Book", bitIndex = 3), // mage training arena minigame
|
||||
AstronomyBook(Items.ASTRONOMY_BOOK_600, intArrayOf(Items.ASTRONOMY_BOOK_600), type = StorableFamily.BOOKCASE, "Astronomy Book", bitIndex = 4, Quests.OBSERVATORY_QUEST), // observatory quest - IMPLEMENTED
|
||||
BatteredBook(Items.BATTERED_BOOK_2886, intArrayOf(Items.BATTERED_BOOK_2886), type = StorableFamily.BOOKCASE, "Book of the Elemental Shield", bitIndex = 5, Quests.ELEMENTAL_WORKSHOP_I), // elemental workshop I quest - IMPLEMENTED
|
||||
BatteredTome(Items.BATTERED_TOME_7634, intArrayOf(Items.BATTERED_TOME_7634), type = StorableFamily.BOOKCASE, "Histories of Hallowland", bitIndex = 6, Quests.IN_AID_OF_THE_MYREQUE), // in aid of myreque quest
|
||||
BeatenBook(Items.BEATEN_BOOK_9717, intArrayOf(Items.BEATEN_BOOK_9717), type = StorableFamily.BOOKCASE, "Book of the Elemental Helm", bitIndex = 7, Quests.ELEMENTAL_WORKSHOP_II), // elemental workshop II quest
|
||||
BigBookOfBangs(Items.BIG_BOOK_OF_BANGS_3230, intArrayOf(Items.BIG_BOOK_OF_BANGS_3230), type = StorableFamily.BOOKCASE, "Big Book of Bangs", bitIndex = 8, Quests.REGICIDE), // regicide quest
|
||||
BindingBook(Items.BINDING_BOOK_730, intArrayOf(Items.BINDING_BOOK_730), type = StorableFamily.BOOKCASE, "Book of Binding", bitIndex = 9, Quests.LEGENDS_QUEST), // legends quest - IMPLEMENTED
|
||||
BirdBook(Items.BIRD_BOOK_10173, intArrayOf(Items.BIRD_BOOK_10173), type = StorableFamily.BOOKCASE, "William Oddity's Guide to the Avian", bitIndex = 10, Quests.EAGLES_PEAK), // eagles peak quest
|
||||
BookOnBaxtorian(Items.BOOK_ON_BAXTORIAN_292, intArrayOf(Items.BOOK_ON_BAXTORIAN_292), type = StorableFamily.BOOKCASE, "Book on Baxtorian", bitIndex = 11, Quests.WATERFALL_QUEST), // waterfall quest - IMPLEMENTED
|
||||
BookOnChemicals(Items.BOOK_ON_CHEMICALS_711, intArrayOf(Items.BOOK_ON_CHEMICALS_711), type = StorableFamily.BOOKCASE, "Volatile chemicals - Experimental Test Notes", bitIndex = 12, Quests.THE_DIG_SITE), // dig site quest - IMPLEMENTED
|
||||
BookOPiracy(Items.BOOK_O_PIRACY_7144, intArrayOf(Items.BOOK_O_PIRACY_7144), type = StorableFamily.BOOKCASE, "Book o' Piracy", bitIndex = 13, Quests.CABIN_FEVER), // cabin fever quest
|
||||
BrewinGuide(Items.BREWIN_GUIDE_8989, intArrayOf(Items.BREWIN_GUIDE_8989), type = StorableFamily.BOOKCASE, "Brewin' Guide", bitIndex = 14), // trouble brewing quest/minigame
|
||||
BurntDiary(Items.BURNT_DIARY_7965, intArrayOf(Items.BURNT_DIARY_7965), type = StorableFamily.BOOKCASE, "Burnt Diary", bitIndex = 15, Quests.ROYAL_TROUBLE), // royal trouble quest
|
||||
CadarnLineage(Items.CADARN_LINEAGE_4209, intArrayOf(Items.CADARN_LINEAGE_4209), type = StorableFamily.BOOKCASE, "Cadarn Lineage", bitIndex = 16, Quests.ROVING_ELVES), // roving elves quest
|
||||
ClockworkBook(Items.CLOCKWORK_BOOK_10594, intArrayOf(Items.CLOCKWORK_BOOK_10594), type = StorableFamily.BOOKCASE, "Clockwork Toys - A Clockwork Mechanism, Chapter 1.0", bitIndex = 17, Quests.COLD_WAR), // cold war quest
|
||||
CocktailGuide(Items.COCKTAIL_GUIDE_2023, intArrayOf(Items.COCKTAIL_GUIDE_2023), type = StorableFamily.BOOKCASE, "The Blurberry Cocktail Guide", bitIndex = 18), // gnome restaurant minigame
|
||||
ConstructionGuide(Items.CONSTRUCTION_GUIDE_8463, intArrayOf(Items.CONSTRUCTION_GUIDE_8463), type = StorableFamily.BOOKCASE, "Guide to Construction", bitIndex = 19), // after you buy a house from estate agent - IMPLEMENTED ish
|
||||
CrumblingTome(Items.CRUMBLING_TOME_4707, intArrayOf(Items.CRUMBLING_TOME_4707), type = StorableFamily.BOOKCASE, "Legend of the Brothers", bitIndex = 20), // barrows minigame
|
||||
CrystalSinging(Items.CRYSTAL_OF_SEREN_4313, intArrayOf(Items.CRYSTAL_OF_SEREN_4313), type = StorableFamily.BOOKCASE, "Crystal singing for beginners", bitIndex = 21, Quests.ROVING_ELVES), // roving elves quest
|
||||
DagonhaiHistory(Items.DAGONHAI_HISTORY_11001, intArrayOf(Items.DAGONHAI_HISTORY_11001), type = StorableFamily.BOOKCASE, "History of the Dagon'hai", bitIndex = 22), // found in varrock castle library
|
||||
DemonicTome(Items.DEMONIC_TOME_6749, intArrayOf(Items.DEMONIC_TOME_6749), type = StorableFamily.BOOKCASE, "The Confessions of Ellemar", bitIndex = 23, Quests.SHADOW_OF_THE_STORM), // shadow of the storm quest
|
||||
DiaryHerbi(Items.DIARY_3395, intArrayOf(Items.DIARY_3395), type = StorableFamily.BOOKCASE, "Diary of Herbi Flax", bitIndex = 24, Quests.SHADES_OF_MORTTON), // shades of mort'ton quest
|
||||
DrozalsJournal(Items.JOURNAL_6755, intArrayOf(Items.JOURNAL_6755), type = StorableFamily.BOOKCASE, "Drozal's Journal", bitIndex = 25, Quests.MAKING_HISTORY), // making history quest
|
||||
DwarvenLore(Items.DWARVEN_LORE_4568, intArrayOf(Items.DWARVEN_LORE_4568), type = StorableFamily.BOOKCASE, "The Arzinian Being of Bordanzan", bitIndex = 26, Quests.BETWEEN_A_ROCK), // between a rock quest
|
||||
EasternDiscovery(Items.EASTERN_DISCOVERY_6075, intArrayOf(Items.EASTERN_DISCOVERY_6075), type = StorableFamily.BOOKCASE, "The Exploration of the Eastern Realm", bitIndex = 27, Quests.MOURNINGS_END_PART_I), // mourning's end I quest
|
||||
EasternSettlement(Items.EASTERN_SETTLEMENT_6077, intArrayOf(Items.EASTERN_SETTLEMENT_6077), type = StorableFamily.BOOKCASE, "The Settlement of the East", bitIndex = 28, Quests.MOURNINGS_END_PART_I), // mourning's end I quest
|
||||
EdernsJournal(Items.EDERNS_JOURNAL_6649, intArrayOf(Items.EDERNS_JOURNAL_6649), type = StorableFamily.BOOKCASE, "Journal of Nissyen Edern", bitIndex = 29, Quests.MOURNINGS_END_PART_II), // mourning's end II quest
|
||||
ExplorersNotes(Items.EXPLORERS_NOTES_11677, intArrayOf(Items.EXPLORERS_NOTES_11677), type = StorableFamily.BOOKCASE, "Beyond Trollheim", bitIndex = 30), // found in keldagrim library
|
||||
EmbalmingManual(Items.EMBALMING_MANUAL_4686, intArrayOf(Items.EMBALMING_MANUAL_4686), type = StorableFamily.BOOKCASE, "The Little Book of Embalming", bitIndex = 31, Quests.ICTHLARINS_LITTLE_HELPER), // icthlarin's little helper quest
|
||||
FarmingManual(Items.FARMING_MANUAL_9903, intArrayOf(Items.FARMING_MANUAL_9903), type = StorableFamily.BOOKCASE, "Farmer Gricoller's Farming Manual", bitIndex = 32, Quests.MY_ARMS_BIG_ADVENTURE), // my arm's big adventure quest
|
||||
FeatheredJournal(Items.FEATHERED_JOURNAL_10179, intArrayOf(Items.FEATHERED_JOURNAL_10179), type = StorableFamily.BOOKCASE, "Feathered Journal of Arthur Artimus", bitIndex = 33, Quests.EAGLES_PEAK), // eagle's peak
|
||||
GameBook(Items.GAME_BOOK_7681, intArrayOf(Items.GAME_BOOK_7681), type = StorableFamily.BOOKCASE, "Party Pete's Bumper Book of Games", bitIndex = 34), // poh game room. I think the bookshelf is the only source for this
|
||||
GhrimsBook(Items.GHRIMS_BOOK_3901, intArrayOf(Items.GHRIMS_BOOK_3901), type = StorableFamily.BOOKCASE, "Managing Thine Kingdom for Noobes", bitIndex = 35, Quests.THRONE_OF_MISCELLANIA), // kingdom of misc quest
|
||||
GiannesCookBook(Items.GIANNES_COOK_BOOK_2167, intArrayOf(Items.GIANNES_COOK_BOOK_2167), type = StorableFamily.BOOKCASE, "Gianne's Cook Book", bitIndex = 36), // gnome retaurant minigame - IMPLEMENTED
|
||||
GlassblowingBook(Items.GLASSBLOWING_BOOK_11656, intArrayOf(Items.GLASSBLOWING_BOOK_11656), type = StorableFamily.BOOKCASE, "Glassblowing Book", bitIndex = 37), // found at the observatory
|
||||
GoblinBook(Items.GOBLIN_BOOK_10999, intArrayOf(Items.GOBLIN_BOOK_10999), type = StorableFamily.BOOKCASE, "The Book of the Big High War God", bitIndex = 38), // dropped by goblins - IMPLEMENTED
|
||||
GoblinSymbolBook(Items.GOBLIN_SYMBOL_BOOK_5009, intArrayOf(Items.GOBLIN_SYMBOL_BOOK_5009), type = StorableFamily.BOOKCASE, "A History of the Goblin Race", bitIndex = 39, Quests.THE_LOST_TRIBE), // lost tribe quest - IMPLEMENTED
|
||||
ElvenBook(Items.THE_GREAT_DIVIDE_6079, intArrayOf(Items.THE_GREAT_DIVIDE_6079), type = StorableFamily.BOOKCASE, "The Great Divide", bitIndex = 40, Quests.MOURNINGS_END_PART_I), // mourning's end I quest
|
||||
GuideBook(Items.GUIDE_BOOK_1856, intArrayOf(Items.GUIDE_BOOK_1856), type = StorableFamily.BOOKCASE, "Tourist Guide to Ardougne", bitIndex = 41), // found by estate agent in ardy - IMPLEMENTED
|
||||
AHandwrittenBook(Items.A_HANDWRITTEN_BOOK_9627, intArrayOf(Items.A_HANDWRITTEN_BOOK_9627), type = StorableFamily.BOOKCASE, "Crystal Singing for Beginners (trans. Oaknock) (Transcribed from the original Elvish by Oaknock the Engineer)", bitIndex = 42, Quests.THE_EYES_OF_GLOUPHRIE), // eyes of glouphrie quest
|
||||
HermansBook(Items.HERMANS_BOOK_7951, intArrayOf(Items.HERMANS_BOOK_7951), type = StorableFamily.BOOKCASE, "Dionysius: A Legend in His Own Lifetime", bitIndex = 43, Quests.SWAN_SONG), // swan song quest
|
||||
HistoryOfIban(Items.HISTORY_OF_IBAN_1494, intArrayOf(Items.HISTORY_OF_IBAN_1494), type = StorableFamily.BOOKCASE, "The Tale of Iban", bitIndex = 44, Quests.UNDERGROUND_PASS), // underground pass quest
|
||||
InstructionManual(Items.INSTRUCTION_MANUAL_5, intArrayOf(Items.INSTRUCTION_MANUAL_5), type = StorableFamily.BOOKCASE, "Dwarf Multicannon Manual", bitIndex = 45, Quests.DWARF_CANNON), // dwarf cannon quest - IMPLEMENTED
|
||||
JossiksJournal(Items.JOURNAL_3845, intArrayOf(Items.JOURNAL_3845), type = StorableFamily.BOOKCASE, "Jossik's Diary", bitIndex = 46, Quests.HORROR_FROM_THE_DEEP), // horror from the deep quest
|
||||
LeatherBook(Items.LEATHER_BOOK_7635, intArrayOf(Items.LEATHER_BOOK_7635), type = StorableFamily.BOOKCASE, "Modern Day Morytania", bitIndex = 47, Quests.IN_AID_OF_THE_MYREQUE), // in aid of the myreque quest
|
||||
Manual(Items.MANUAL_3847, intArrayOf(Items.MANUAL_3847), type = StorableFamily.BOOKCASE, "Lighthouse Manual", bitIndex = 48, Quests.HORROR_FROM_THE_DEEP), // horror from the deep quest
|
||||
MoonclanManual(Items.MOONCLAN_MANUAL_9078, intArrayOf(Items.MOONCLAN_MANUAL_9078), type = StorableFamily.BOOKCASE, "Basics of Magic", bitIndex = 49, Quests.LUNAR_DIPLOMACY), // lunar diplomacy quest
|
||||
MyNotes(Items.MY_NOTES_11339, intArrayOf(Items.MY_NOTES_11339), type = StorableFamily.BOOKCASE, "'My notes'", bitIndex = 50), // during barbarian training
|
||||
OldJournal(Items.OLD_JOURNAL_1493, intArrayOf(Items.OLD_JOURNAL_1493), type = StorableFamily.BOOKCASE, "The Journal of Randas", bitIndex = 51, Quests.UNDERGROUND_PASS), // underground pass quest
|
||||
OldTome(Items.OLD_TOME_13593, intArrayOf(Items.OLD_TOME_13593), type = StorableFamily.BOOKCASE, "Old Tome", bitIndex = 52, Quests.MEETING_HISTORY), // meeting history quest
|
||||
PrayerBook(Items.PRAYER_BOOK_10890, intArrayOf(Items.PRAYER_BOOK_10890), type = StorableFamily.BOOKCASE, "Prayer of Deliverance from Poisons", bitIndex = 53, Quests.THE_GREAT_BRAIN_ROBBERY), // the great brain robbery quest
|
||||
PieRecipeBook(Items.PIE_RECIPE_BOOK_7162, intArrayOf(Items.PIE_RECIPE_BOOK_7162), type = StorableFamily.BOOKCASE, "Pie Recipe Book", bitIndex = 54), // from romily weaklax
|
||||
PrifddinasHistory(Items.PRIFDDINAS_HISTORY_6073, intArrayOf(Items.PRIFDDINAS_HISTORY_6073), type = StorableFamily.BOOKCASE, "The Creation of Prifddinas", bitIndex = 55), // found in lletya bookshelves
|
||||
QueenHelpBook(Items.QUEEN_HELP_BOOK_10562, intArrayOf(Items.QUEEN_HELP_BOOK_10562), type = StorableFamily.BOOKCASE, "Queen Help Book", bitIndex = 56), // barbarian assault minigame
|
||||
RatPitsManual(Items.BOOK_6767, intArrayOf(Items.BOOK_6767), type = StorableFamily.BOOKCASE, "The RatPits Manual", bitIndex = 57, Quests.RATCATCHERS), // rat catchers quest
|
||||
ScabariteNotes(Items.SCABARITE_NOTES_11975, intArrayOf(Items.SCABARITE_NOTES_11975), type = StorableFamily.BOOKCASE, "My Notes, Archaeological exploration and the cult of Scabaras", bitIndex = 58, Quests.DEALING_WITH_SCABARAS), // dealing with scabaras quest
|
||||
SecurityBook(Items.SECURITY_BOOK_9003, intArrayOf(Items.SECURITY_BOOK_9003), type = StorableFamily.BOOKCASE, "" + GameWorld.settings!!.name + " Account Security", bitIndex = 59), // from stronghold of security - IMPLEMENTED
|
||||
ShamansTome(Items.SHAMANS_TOME_729, intArrayOf(Items.SHAMANS_TOME_729), type = StorableFamily.BOOKCASE, "Shaman's Tome", bitIndex = 60, Quests.LEGENDS_QUEST), // legends quest - IMPLEMENTED
|
||||
TheShieldOfArravBook(Items.BOOK_757, intArrayOf(Items.BOOK_757), type = StorableFamily.BOOKCASE, "The Shield of Arrav", bitIndex = 61, Quests.SHIELD_OF_ARRAV), // shield of arrav quest - IMPLEMENTED
|
||||
SinkethsDiary(Items.SINKETHS_DIARY_11002, intArrayOf(Items.SINKETHS_DIARY_11002), type = StorableFamily.BOOKCASE, "Sin'keth's Diary", bitIndex = 62, Quests.WHAT_LIES_BELOW), // what lies below quest - IMPLEMENTED
|
||||
StrongholdNotes(Items.STRONGHOLD_NOTES_9004, intArrayOf(Items.STRONGHOLD_NOTES_9004), type = StorableFamily.BOOKCASE, "Stronghold of Security - Notes", bitIndex = 63), // from stronghold of security - IMPLEMENTED
|
||||
TarnsDiary(Items.TARNS_DIARY_10587, intArrayOf(Items.TARNS_DIARY_10587), type = StorableFamily.BOOKCASE, "The Diary of Tarn Razorlor", bitIndex = 64), // lair of tarn miniquest
|
||||
Translation(Items.TRANSLATION_4655, intArrayOf(Items.TRANSLATION_4655), type = StorableFamily.BOOKCASE, "Translation Primer (Four Diamonds Translation)", bitIndex = 65, Quests.DESERT_TREASURE), // desert treasure quest - IMPLEMENTED
|
||||
TranslationBook(Items.TRANSLATION_BOOK_784, intArrayOf(Items.TRANSLATION_BOOK_784), type = StorableFamily.BOOKCASE, "Gnome-English Translation Dictionary", bitIndex = 66, Quests.THE_EYES_OF_GLOUPHRIE), // eyes of glouphrie quest - IMPLEMENTED
|
||||
TzhaarTouristGuide(Items.TZHAAR_TOURIST_GUIDE_13244, intArrayOf(Items.TZHAAR_TOURIST_GUIDE_13244), type = StorableFamily.BOOKCASE, "Tzhaar Tourist Guide", bitIndex = 67, Quests.TOKTZ_KET_DILL), // toktz-ket-dill quest
|
||||
VarmensNotes(Items.VARMENS_NOTES_4616, intArrayOf(Items.VARMENS_NOTES_4616), type = StorableFamily.BOOKCASE, "Notes from Varmen's Expedition to Uzer", bitIndex = 68, Quests.THE_GOLEM), // the golem quest - IMPLEMENTED
|
||||
WitchesDiary(Items.DIARY_2408, intArrayOf(Items.DIARY_2408), type = StorableFamily.BOOKCASE, "Witch's Diary", bitIndex = 69, Quests.WITCHS_HOUSE), // witch's house quest - IMPLEMENTED
|
||||
YewnocksNotes(Items.YEWNOCKS_NOTES_11750, intArrayOf(Items.YEWNOCKS_NOTES_11750), type = StorableFamily.BOOKCASE, "Yewnock's Notes on Crystals", bitIndex = 70, Quests.THE_PATH_OF_GLOUPHRIE), // path of glouphrie quest
|
||||
|
||||
// Capes - https://runescape.wiki/w/Cape_rack?oldid=848849
|
||||
LegendsCape(Items.CAPE_OF_LEGENDS_1052, intArrayOf(Items.CAPE_OF_LEGENDS_1052), type = StorableFamily.CAPE_RACK, "Legends cape"),
|
||||
ObsidianCape(Items.OBSIDIAN_CAPE_6568, intArrayOf(Items.OBSIDIAN_CAPE_6568), type = StorableFamily.CAPE_RACK),
|
||||
FireCape(Items.FIRE_CAPE_6570, intArrayOf(Items.FIRE_CAPE_6570), type = StorableFamily.CAPE_RACK),
|
||||
TeamCape(Items.TEAM_1_CAPE_10638, (4315..4413 step 2).toList().toIntArray(), type = StorableFamily.CAPE_RACK, "Wilderness cape"),
|
||||
GodCape(Items.GUTHIX_CAPE_2413, intArrayOf(Items.GUTHIX_CAPE_2413, Items.SARADOMIN_CAPE_2412, Items.ZAMORAK_CAPE_2414), type = StorableFamily.CAPE_RACK, "God cape"),
|
||||
AttackCape(Items.ATTACK_CAPE_10639, intArrayOf(Items.ATTACK_CAPE_9747, Items.ATTACK_HOOD_9749, Items.ATTACK_CAPET_9748), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
DefenceCape(Items.DEFENCE_CAPE_10641, intArrayOf(Items.DEFENCE_CAPE_9753, Items.DEFENCE_CAPET_9754, Items.DEFENCE_HOOD_9755), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
StrengthCape(Items.STRENGTH_CAPE_10640, intArrayOf(Items.STRENGTH_CAPE_9750, Items.STRENGTH_CAPET_9751, Items.STRENGTH_HOOD_9752), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
HitpointsCape(Items.HITPOINTS_CAPE_10647, intArrayOf(Items.HITPOINTS_CAPE_9768, Items.HITPOINTS_CAPET_9769, Items.HITPOINTS_HOOD_9770), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
AgilityCape(Items.AGILITY_CAPE_10648, intArrayOf(Items.AGILITY_CAPE_9771, Items.AGILITY_CAPET_9772, Items.AGILITY_HOOD_9773), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
CookingCape(Items.COOKING_CAPE_10658, intArrayOf(Items.COOKING_CAPE_9801, Items.COOKING_CAPET_9802, Items.COOKING_HOOD_9803), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
ConstructionCape(Items.CONSTRUCT_CAPE_10654, intArrayOf(Items.CONSTRUCT_CAPE_9789, Items.CONSTRUCT_CAPET_9790, Items.CONSTRUCT_HOOD_9791), type = StorableFamily.CAPE_RACK_SKILL, "Construction cape"),
|
||||
CraftingCape(Items.CRAFTING_CAPE_10651, intArrayOf(Items.CRAFTING_CAPE_9780, Items.CRAFTING_CAPET_9781, Items.CRAFTING_HOOD_9782), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
FarmingCape(Items.FARMING_CAPE_10661, intArrayOf(Items.FARMING_CAPE_9810, Items.FARMING_CAPET_9811, Items.FARMING_HOOD_9812), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
FiremakingCape(Items.FIREMAKING_CAPE_10659, intArrayOf(Items.FIREMAKING_CAPE_9804, Items.FIREMAKING_CAPET_9805, Items.FIREMAKING_HOOD_9806), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
FishingCape(Items.FISHING_CAPE_10657, intArrayOf(Items.FISHING_CAPE_9798, Items.FISHING_CAPET_9799, Items.FISHING_HOOD_9800), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
FletchingCape(Items.FLETCHING_CAPE_10652, intArrayOf(Items.FLETCHING_CAPE_9783, Items.FLETCHING_CAPET_9784, Items.FLETCHING_HOOD_9785), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
HerbloreCape(Items.HERBLORE_CAPE_10649, intArrayOf(Items.HERBLORE_CAPE_9774, Items.HERBLORE_CAPET_9775, Items.HERBLORE_HOOD_9776), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
MagicCape(Items.MAGIC_CAPE_10644, intArrayOf(Items.MAGIC_CAPE_9762, Items.MAGIC_CAPET_9763, Items.MAGIC_HOOD_9764), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
MiningCape(Items.MINING_CAPE_10655, intArrayOf(Items.MINING_CAPE_9792, Items.MINING_CAPET_9793, Items.MINING_HOOD_9794), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
PrayerCape(Items.PRAYER_CAPE_10643, intArrayOf(Items.PRAYER_CAPE_9759, Items.PRAYER_CAPET_9760, Items.PRAYER_HOOD_9761), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
RangingCape(Items.RANGING_CAPE_10642, intArrayOf(Items.RANGING_CAPE_9756, Items.RANGING_CAPET_9757, Items.RANGING_HOOD_9758), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
RunecraftCape(Items.RUNECRAFT_CAPE_10645, intArrayOf(Items.RUNECRAFT_CAPE_9765, Items.RUNECRAFT_CAPET_9766, Items.RUNECRAFTING_HOOD_9767), type = StorableFamily.CAPE_RACK_SKILL, "Runecrafting cape"),
|
||||
SlayerCape(Items.SLAYER_CAPE_10653, intArrayOf(Items.SLAYER_CAPE_9786, Items.SLAYER_CAPET_9787, Items.SLAYER_HOOD_9788), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
SmithingCape(Items.SMITHING_CAPE_10656, intArrayOf(Items.SMITHING_CAPE_9795, Items.SMITHING_CAPET_9796, Items.SMITHING_HOOD_9797), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
ThievingCape(Items.THIEVING_CAPE_10650, intArrayOf(Items.THIEVING_CAPE_9777, Items.THIEVING_CAPET_9778, Items.THIEVING_HOOD_9779), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
WoodcuttingCape(Items.WOODCUTTING_CAPE_10660, intArrayOf(Items.WOODCUTTING_CAPE_9807, Items.WOODCUT_CAPET_9808, Items.WOODCUTTING_HOOD_9809), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
HunterCape(Items.HUNTER_CAPE_10646, intArrayOf(Items.HUNTER_CAPE_9948, Items.HUNTER_CAPET_9949, Items.HUNTER_HOOD_9950), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
QuestCape(Items.QUEST_POINT_CAPE_10662, intArrayOf(Items.QUEST_POINT_CAPE_9813, Items.QUEST_POINT_HOOD_9814), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
SummoningCape(Items.SUMMONING_CAPE_12524, intArrayOf(Items.SUMMONING_CAPE_12169, Items.SUMMONING_CAPET_12170, Items.SUMMONING_HOOD_12171), type = StorableFamily.CAPE_RACK_SKILL),
|
||||
SpottedCape(Items.SPOTTED_CAPE_10663, intArrayOf(Items.SPOTTED_CAPE_10663), type = StorableFamily.CAPE_RACK, "Spotted hunting cape"),
|
||||
SpottierCape(Items.SPOTTIER_CAPE_10664, intArrayOf(Items.SPOTTIER_CAPE_10664), type = StorableFamily.CAPE_RACK, "Spottier hunting cape"),
|
||||
|
||||
// Fancy Dress - https://runescape.wiki/w/Fancy_dress_box?oldid=848873
|
||||
MimeCostume(Items.MIME_MASK_10629, intArrayOf(Items.MIME_MASK_3057, Items.MIME_TOP_3058, Items.MIME_LEGS_3059, Items.MIME_GLOVES_3060, Items.MIME_BOOTS_3061), type = StorableFamily.FANCY_DRESS, "Mime Costume"),
|
||||
RoyalFrogCostume(Items.PRINCESS_BLOUSE_10630, intArrayOf(Items.PRINCE_TUNIC_6184, Items.PRINCE_LEGGINGS_6185, Items.PRINCESS_BLOUSE_6186, Items.PRINCESS_SKIRT_6187), type = StorableFamily.FANCY_DRESS, "Royal frog costume"),
|
||||
FrogMask(Items.FROG_MASK_10721, intArrayOf(Items.FROG_MASK_6188), type = StorableFamily.FANCY_DRESS),
|
||||
ZombieOutfit(Items.ZOMBIE_SHIRT_10631, intArrayOf(Items.ZOMBIE_MASK_7594, Items.ZOMBIE_SHIRT_7592, Items.ZOMBIE_TROUSERS_7593, Items.ZOMBIE_GLOVES_7595, Items.ZOMBIE_BOOTS_7596), type = StorableFamily.FANCY_DRESS, "Zombie Outfit"),
|
||||
CamoOutfit(Items.CAMO_TOP_10632, intArrayOf(Items.CAMO_HELMET_6656, Items.CAMO_TOP_6654, Items.CAMO_BOTTOMS_6655), type = StorableFamily.FANCY_DRESS, "Camo outfit"),
|
||||
LederhosenOutfit(Items.LEDERHOSEN_TOP_10633, intArrayOf(Items.LEDERHOSEN_TOP_6180, Items.LEDERHOSEN_SHORTS_6181, Items.LEDERHOSEN_HAT_6182), type = StorableFamily.FANCY_DRESS, "Lederhosen outfit"),
|
||||
ShadeRobes(Items.SHADE_ROBE_10634, intArrayOf(Items.SHADE_ROBE_546, Items.SHADE_ROBE_548), type = StorableFamily.FANCY_DRESS, "Shade robes"),
|
||||
|
||||
// Toys - https://runescape.wiki/w/Toy_box?oldid=848855
|
||||
BunnyEars(Items.BUNNY_EARS_10734, intArrayOf(Items.BUNNY_EARS_1037), type = StorableFamily.TOY_BOX),
|
||||
Scythe(Items.SCYTHE_10735, intArrayOf(Items.SCYTHE_1419), type = StorableFamily.TOY_BOX),
|
||||
YoYo(Items.YO_YO_10733, intArrayOf(Items.YO_YO_4079), type = StorableFamily.TOY_BOX),
|
||||
RubberChicken(Items.RUBBER_CHICKEN_10732, intArrayOf(Items.RUBBER_CHICKEN_4566), type = StorableFamily.TOY_BOX),
|
||||
ZombieHead(Items.ZOMBIE_HEAD_10731, intArrayOf(Items.ZOMBIE_HEAD_6722), type = StorableFamily.TOY_BOX),
|
||||
EasterRing(Items.EASTER_RING_10729, intArrayOf(Items.EASTER_RING_7927), type = StorableFamily.TOY_BOX),
|
||||
BobbleHat(Items.BOBBLE_HAT_9815, intArrayOf(Items.BOBBLE_HAT_6856), type = StorableFamily.TOY_BOX),
|
||||
BobbleScarf(Items.BOBBLE_SCARF_9816, intArrayOf(Items.BOBBLE_SCARF_6857), type = StorableFamily.TOY_BOX),
|
||||
JesterHat(Items.JESTER_HAT_6858, intArrayOf(Items.JESTER_HAT_6858), type = StorableFamily.TOY_BOX),
|
||||
JesterScarf(Items.JESTER_SCARF_6859, intArrayOf(Items.JESTER_SCARF_6859), type = StorableFamily.TOY_BOX),
|
||||
TriJesterHat(Items.TRI_JESTER_HAT_6860, intArrayOf(Items.TRI_JESTER_HAT_6860), type = StorableFamily.TOY_BOX),
|
||||
TriJesterScarf(Items.TRI_JESTER_SCARF_6861, intArrayOf(Items.TRI_JESTER_SCARF_6861), type = StorableFamily.TOY_BOX),
|
||||
WoollyHat(Items.WOOLLY_HAT_6862, intArrayOf(Items.WOOLLY_HAT_6862), type = StorableFamily.TOY_BOX),
|
||||
WoollyScarf(Items.WOOLLY_SCARF_6863, intArrayOf(Items.WOOLLY_SCARF_6863), type = StorableFamily.TOY_BOX),
|
||||
Marionette(Items.BLUE_MARIONETTE_10730, intArrayOf(Items.BLUE_MARIONETTE_6865,Items.GREEN_MARIONETTE_6866,Items.RED_MARIONETTE_6867), type = StorableFamily.TOY_BOX), // this authentically displays as 'blue marionette' in the source video
|
||||
JackLanternMask(Items.JACK_LANTERN_MASK_10723, intArrayOf(Items.JACK_LANTERN_MASK_9920), type = StorableFamily.TOY_BOX),
|
||||
SkeletonBoots(Items.SKELETON_BOOTS_10724, intArrayOf(Items.SKELETON_BOOTS_9921), type = StorableFamily.TOY_BOX),
|
||||
SkeletonGloves(Items.SKELETON_GLOVES_10725, intArrayOf(Items.SKELETON_GLOVES_9922), type = StorableFamily.TOY_BOX),
|
||||
SkeletonLeggings(Items.SKELETON_LEGGINGS_10726, intArrayOf(Items.SKELETON_LEGGINGS_9923), type = StorableFamily.TOY_BOX),
|
||||
SkeletonShirt(Items.SKELETON_SHIRT_10727, intArrayOf(Items.SKELETON_SHIRT_9924), type = StorableFamily.TOY_BOX),
|
||||
SkeletonMask(Items.SKELETON_MASK_10728, intArrayOf(Items.SKELETON_MASK_9925), type = StorableFamily.TOY_BOX),
|
||||
ReindeerHat(Items.REINDEER_HAT_10722, intArrayOf(Items.REINDEER_HAT_10507), type = StorableFamily.TOY_BOX),
|
||||
WintumberTree(Items.WINTUMBER_TREE_10508, intArrayOf(Items.WINTUMBER_TREE_10508), type = StorableFamily.TOY_BOX),
|
||||
ChickenFeet(Items.CHICKEN_FEET_11019, intArrayOf(Items.CHICKEN_FEET_11019), type = StorableFamily.TOY_BOX),
|
||||
ChickenLegs(Items.CHICKEN_LEGS_11022, intArrayOf(Items.CHICKEN_LEGS_11022), type = StorableFamily.TOY_BOX),
|
||||
ChickenWings(Items.CHICKEN_WINGS_11020, intArrayOf(Items.CHICKEN_WINGS_11020), type = StorableFamily.TOY_BOX),
|
||||
ChickenHead(Items.CHICKEN_HEAD_11021, intArrayOf(Items.CHICKEN_HEAD_11021), type = StorableFamily.TOY_BOX),
|
||||
GrimReaperHood(Items.GRIM_REAPER_HOOD_11789, intArrayOf(Items.GRIM_REAPER_HOOD_11789), type = StorableFamily.TOY_BOX),
|
||||
SnowGlobe(Items.SNOW_GLOBE_11949, intArrayOf(Items.SNOW_GLOBE_11949), type = StorableFamily.TOY_BOX),
|
||||
ChocaticeCape(Items.CHOCATRICE_CAPE_12634, intArrayOf(Items.CHOCATRICE_CAPE_12645), type = StorableFamily.TOY_BOX),
|
||||
WarlockTop(Items.WARLOCK_TOP_14076, intArrayOf(Items.WARLOCK_TOP_14076, Items.WITCH_TOP_14078), type = StorableFamily.TOY_BOX),
|
||||
WarlockLegs(Items.WARLOCK_LEGS_14077, intArrayOf(Items.WARLOCK_LEGS_14077, Items.WITCH_SKIRT_14079), type = StorableFamily.TOY_BOX),
|
||||
WarlockCloak(Items.WARLOCK_CLOAK_14081, intArrayOf(Items.WARLOCK_CLOAK_14081, Items.WITCH_CLOAK_14088), type = StorableFamily.TOY_BOX),
|
||||
SantaCostumeTop(Items.SANTA_COSTUME_TOP_14595, intArrayOf(Items.SANTA_COSTUME_TOP_14595, Items.SANTA_COSTUME_TOP_14600), type = StorableFamily.TOY_BOX),
|
||||
SantaCostumeLegs(Items.SANTA_COSTUME_LEGS_14603, intArrayOf(Items.SANTA_COSTUME_LEGS_14603, Items.SANTA_COSTUME_LEGS_14604), type = StorableFamily.TOY_BOX),
|
||||
SantaCostumeGloves(Items.SANTA_COSTUME_GLOVES_14602, intArrayOf(Items.SANTA_COSTUME_GLOVES_14602), type = StorableFamily.TOY_BOX),
|
||||
SantaCostumeBoots(Items.SANTA_COSTUME_BOOTS_14605, intArrayOf(Items.SANTA_COSTUME_BOOTS_14605), type = StorableFamily.TOY_BOX),
|
||||
IceAmulet(Items.ICE_AMULET_14596, intArrayOf(Items.ICE_AMULET_14596), type = StorableFamily.TOY_BOX),
|
||||
Cornucopia(Items.CORNUCOPIA_14570, intArrayOf(Items.CORNUCOPIA_14537), type = StorableFamily.TOY_BOX),
|
||||
|
||||
// Treasure Trails - low - https://runescape.wiki/w/Treasure_chest?oldid=848831
|
||||
TrimmedBlackArmour(Items.BLACK_PLATEBODY_T_10690, intArrayOf(Items.BLACK_FULL_HELMT_2587, Items.BLACK_PLATEBODY_T_2583, Items.BLACK_PLATELEGS_T_2585, Items.BLACK_KITESHIELD_T_2589, Items.BLACK_PLATESKIRT_T_3472), type = StorableFamily.TREASURE_CHEST_LOW, "Trimmed black armour"),
|
||||
GoldTrimmedBlackArmour(Items.BLACK_PLATEBODY_G_10691, intArrayOf(Items.BLACK_FULL_HELMG_2595, Items.BLACK_PLATEBODY_G_2591, Items.BLACK_PLATELEGS_G_2593, Items.BLACK_KITESHIELD_G_2597, Items.BLACK_PLATESKIRT_G_3473), type = StorableFamily.TREASURE_CHEST_LOW, "Gold-trimmed black armour"),
|
||||
BlackHeraldicH1(Items.BLACK_HELM_H1_10699, intArrayOf(Items.BLACK_HELM_H1_10306), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"),
|
||||
BlackHeraldicH2(Items.BLACK_HELM_H2_10700, intArrayOf(Items.BLACK_HELM_H2_10308), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"),
|
||||
BlackHeraldicH3(Items.BLACK_HELM_H3_10701, intArrayOf(Items.BLACK_HELM_H3_10310), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"),
|
||||
BlackHeraldicH4(Items.BLACK_HELM_H4_10702, intArrayOf(Items.BLACK_HELM_H4_10312), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"),
|
||||
BlackHeraldicH5(Items.BLACK_HELM_H5_10703, intArrayOf(Items.BLACK_HELM_H5_10314), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"),
|
||||
BlackHeraldicKiteshieldH1(Items.BLACK_SHIELDH1_10665, intArrayOf(Items.BLACK_SHIELDH1_7332), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"),
|
||||
BlackHeraldicKiteshieldH2(Items.BLACK_SHIELDH2_10668, intArrayOf(Items.BLACK_SHIELDH2_7338), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"),
|
||||
BlackHeraldicKiteshieldH3(Items.BLACK_SHIELDH3_10671, intArrayOf(Items.BLACK_SHIELDH3_7344), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"),
|
||||
BlackHeraldicKiteshieldH4(Items.BLACK_SHIELDH4_10674, intArrayOf(Items.BLACK_SHIELDH4_7350), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"),
|
||||
BlackHeraldicKiteshieldH5(Items.BLACK_SHIELDH5_10677, intArrayOf(Items.BLACK_SHIELDH5_7356), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"),
|
||||
TrimmedStuddedArmour(Items.STUDDED_BODY_T_10681, intArrayOf(Items.STUDDED_BODY_T_7364, Items.STUDDED_CHAPS_T_7368), type = StorableFamily.TREASURE_CHEST_LOW, "Fur-trimmed studded leather"),
|
||||
GoldTrimmedStuddedArmour(Items.STUDDED_BODY_G_10680, intArrayOf(Items.STUDDED_BODY_G_7362, Items.STUDDED_CHAPS_G_7366), type = StorableFamily.TREASURE_CHEST_LOW, "Gold-trimmed studded leather"),
|
||||
TrimmedWizardRobes(Items.WIZARD_ROBE_T_10687, intArrayOf(Items.WIZARD_HAT_T_7396, Items.WIZARD_ROBE_T_7392, Items.BLUE_SKIRT_T_7388), type = StorableFamily.TREASURE_CHEST_LOW, "Trimmed wizard's robes"),
|
||||
GoldTrimmedWizardRobes(Items.WIZARD_ROBE_G_10686, intArrayOf(Items.WIZARD_HAT_G_7394, Items.WIZARD_ROBE_G_7390, Items.BLUE_SKIRT_G_7386), type = StorableFamily.TREASURE_CHEST_LOW, "Gold-trimmed wizard's robes"),
|
||||
WizardBoots(Items.WIZARD_BOOTS_10689, intArrayOf(Items.WIZARD_BOOTS_2579), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
TrimmedAmuletOfMagic(Items.AMULET_OF_MAGICT_10738, intArrayOf(Items.AMULET_OF_MAGICT_10366), type = StorableFamily.TREASURE_CHEST_LOW, "Trimmed amulet of magic"),
|
||||
HighwaymanMask(Items.HIGHWAYMAN_MASK_10692, intArrayOf(Items.HIGHWAYMAN_MASK_2631), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
Pantaloons(Items.PANTALOONS_10744, intArrayOf(Items.PANTALOONS_10396), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
PowderedWig(Items.A_POWDERED_WIG_10740, intArrayOf(Items.A_POWDERED_WIG_10392), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
FlaredTrousers(Items.FLARED_TROUSERS_10742, intArrayOf(Items.FLARED_TROUSERS_10394), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
SleepingCap(Items.SLEEPING_CAP_10746, intArrayOf(Items.SLEEPING_CAP_10398), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
BlackBeret(Items.BLACK_BERET_10694, intArrayOf(Items.BLACK_BERET_2635), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
WhiteBeret(Items.WHITE_BERET_10695, intArrayOf(Items.WHITE_BERET_2637), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
BlueBeret(Items.BLUE_BERET_10693, intArrayOf(Items.BLUE_BERET_2633), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
BobTheCatShirt(Items.BOB_SHIRT_10714, intArrayOf(Items.BOB_SHIRT_10316), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (red)"),
|
||||
BobTheCatShirt1(Items.BOB_SHIRT_10715, intArrayOf(Items.BOB_SHIRT_10318), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (blue)"),
|
||||
BobTheCatShirt2(Items.BOB_SHIRT_10716, intArrayOf(Items.BOB_SHIRT_10320), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (green)"),
|
||||
BobTheCatShirt3(Items.BOB_SHIRT_10717, intArrayOf(Items.BOB_SHIRT_10322), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (black)"),
|
||||
BobTheCatShirt4(Items.BOB_SHIRT_10718, intArrayOf(Items.BOB_SHIRT_10324), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (purple)"),
|
||||
RedElegant(Items.ELEGANT_SHIRT_10750, intArrayOf(Items.RED_ELE_SHIRT_10404, Items.RED_ELE_LEGS_10406, Items.RED_ELE_BLOUSE_10424, Items.RED_ELE_SKIRT_10426), type = StorableFamily.TREASURE_CHEST_LOW, "Elegant clothes (red)"),
|
||||
BlueElegant(Items.ELEGANT_SHIRT_10752, intArrayOf(Items.BLUE_ELE_SHIRT_10408, Items.BLUE_ELE_LEGS_10410, Items.BLUE_ELE_BLOUSE_10428, Items.BLUE_ELE_SKIRT_10430), type = StorableFamily.TREASURE_CHEST_LOW, "Elegant clothes (blue)"),
|
||||
GreenElegant(Items.ELEGANT_SHIRT_10754, intArrayOf(Items.GREEN_ELE_SHIRT_10412,Items.GREEN_ELE_LEGS_10414, Items.GREEN_ELE_BLOUSE_10432, Items.GREEN_ELE_SKIRT_10434), type = StorableFamily.TREASURE_CHEST_LOW, "Elegant clothes (green)"),
|
||||
BeretAndMask(Items.BERET_MASK_11278, intArrayOf(Items.BERET_AND_MASK_11282), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
BlackCane(Items.BLACK_CANE_13163, intArrayOf(Items.BLACK_CANE_13095), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
SpikedHelmet(Items.SPIKED_HELMET_13168, intArrayOf(Items.SPIKED_HELMET_13105), type = StorableFamily.TREASURE_CHEST_LOW),
|
||||
|
||||
// Treasure Trails - medium - https://runescape.wiki/w/Treasure_chest?oldid=848831
|
||||
TrimmedAdamantiteArmour(Items.ADAM_PLATEBODY_T_10697, intArrayOf(Items.ADAM_FULL_HELMT_2605, Items.ADAM_PLATEBODY_T_2599, Items.ADAM_PLATELEGS_T_2601, Items.ADAM_KITESHIELD_T_2603, Items.ADAM_PLATESKIRT_T_3474), type = StorableFamily.TREASURE_CHEST_MED, "Trimmed adamantite armour"),
|
||||
GoldTrimmedAdamantiteArmour(Items.ADAM_PLATEBODY_G_10698, intArrayOf(Items.ADAM_FULL_HELMG_2613, Items.ADAM_PLATEBODY_G_2607, Items.ADAM_PLATELEGS_G_2609, Items.ADAM_KITESHIELD_G_2611, Items.ADAM_PLATESKIRT_G_3475), type = StorableFamily.TREASURE_CHEST_MED, "Gold-trimmed adamantite armour"),
|
||||
AdamantHeraldicH1(Items.ADAMANT_HELM_H1_10709, intArrayOf(Items.ADAMANT_HELM_H1_10296), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"),
|
||||
AdamantHeraldicH2(Items.ADAMANT_HELM_H2_10710, intArrayOf(Items.ADAMANT_HELM_H2_10298), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"),
|
||||
AdamantHeraldicH3(Items.ADAMANT_HELM_H3_10711, intArrayOf(Items.ADAMANT_HELM_H3_10300), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"),
|
||||
AdamantHeraldicH4(Items.ADAMANT_HELM_H4_10712, intArrayOf(Items.ADAMANT_HELM_H4_10302), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"),
|
||||
AdamantHeraldicH5(Items.ADAMANT_HELM_H5_10713, intArrayOf(Items.ADAMANT_HELM_H5_10304), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"),
|
||||
AdamantKiteshieldH1(Items.ADAMANT_SHIELDH1_10666, intArrayOf(Items.ADAMANT_SHIELDH1_7334), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"),
|
||||
AdamantKiteshieldH2(Items.ADAMANT_SHIELDH2_10669, intArrayOf(Items.ADAMANT_SHIELDH2_7340), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"),
|
||||
AdamantKiteshieldH3(Items.ADAMANT_SHIELDH3_10672, intArrayOf(Items.ADAMANT_SHIELDH3_7346), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"),
|
||||
AdamantKiteshieldH4(Items.ADAMANT_SHIELDH4_10675, intArrayOf(Items.ADAMANT_SHIELDH4_7352), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"),
|
||||
AdamantKiteshieldH5(Items.ADAMANT_SHIELDH5_10678, intArrayOf(Items.ADAMANT_SHIELDH5_7358), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"),
|
||||
TrimmedGreenDhide(Items.DHIDE_BODY_T_10683, intArrayOf(Items.DHIDE_BODY_T_7372, Items.DHIDE_CHAPS_T_7380), type = StorableFamily.TREASURE_CHEST_MED, "Trimmed green dragonhide armour"),
|
||||
GoldTrimmedGreenDhide(Items.DHIDE_BODYG_10682, intArrayOf(Items.DHIDE_BODYG_7370, Items.DHIDE_CHAPS_G_7378), type = StorableFamily.TREASURE_CHEST_MED, "Gold-trimmed green dragonhide armour"),
|
||||
RangerBoots(Items.RANGER_BOOTS_10696, intArrayOf(Items.RANGER_BOOTS_2577), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
TrimmedAmuletOfStrength(Items.STRENGTH_AMULETT_10736, intArrayOf(Items.STRENGTH_AMULETT_10364), type = StorableFamily.TREASURE_CHEST_MED, "Trimmed amulet of strength"),
|
||||
RedHeadband(Items.RED_HEADBAND_10768, intArrayOf(Items.RED_HEADBAND_2645), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
BlackHeadband(Items.BLACK_HEADBAND_10770, intArrayOf(Items.BLACK_HEADBAND_2647), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
BrownHeadband(Items.BROWN_HEADBAND_10772, intArrayOf(Items.BROWN_HEADBAND_2649), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
RedBoater(Items.RED_BOATER_10758, intArrayOf(Items.RED_BOATER_7319), type = StorableFamily.TREASURE_CHEST_MED, "Red straw boater"),
|
||||
OrangeBoater(Items.ORANGE_BOATER_10760, intArrayOf(Items.ORANGE_BOATER_7321), type = StorableFamily.TREASURE_CHEST_MED, "Orange straw boater"),
|
||||
GreenBoater(Items.GREEN_BOATER_10762, intArrayOf(Items.GREEN_BOATER_7323), type = StorableFamily.TREASURE_CHEST_MED, "Green straw boater"),
|
||||
BlueBoater(Items.BLUE_BOATER_10764, intArrayOf(Items.BLUE_BOATER_7325), type = StorableFamily.TREASURE_CHEST_MED, "Blue straw boater"),
|
||||
BlackBoater(Items.BLACK_BOATER_10766, intArrayOf(Items.BLACK_BOATER_7327), type = StorableFamily.TREASURE_CHEST_MED, "Black straw boater"),
|
||||
BlackElegant(Items.ELEGANT_SHIRT_10748, intArrayOf(Items.BLACK_ELE_SHIRT_10400, Items.BLACK_ELE_LEGS_10402, Items.WHITE_ELE_BLOUSE_10420, Items.WHITE_ELE_SKIRT_10422), type = StorableFamily.TREASURE_CHEST_MED, "Elegant clothes (black)"),
|
||||
PurpleElegant(Items.ELEGANT_SHIRT_10756, intArrayOf(Items.PURPLE_ELE_BLOUSE_10436, Items.PURPLE_ELE_SKIRT_10438), type = StorableFamily.TREASURE_CHEST_MED, "Elegant clothes (purple)"),
|
||||
AdamantCane(Items.ADAMANT_CANE_13164, intArrayOf(Items.ADAMANT_CANE_13097), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
SheepMask(Items.SHEEP_MASK_13169, intArrayOf(Items.SHEEP_MASK_13107), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
BatMask(Items.BAT_MASK_13171, intArrayOf(Items.BAT_MASK_13111), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
WolfMask(Items.WOLF_MASK_13173, intArrayOf(Items.WOLF_MASK_13115), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
PenguinMask(Items.PENGUIN_MASK_13170, intArrayOf(Items.PENGUIN_MASK_13109), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
CatMask(Items.CAT_MASK_13172, intArrayOf(Items.CAT_MASK_13113), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
PithHelmet(Items.PITH_HELMET_13167, intArrayOf(Items.PITH_HELMET_13103), type = StorableFamily.TREASURE_CHEST_MED),
|
||||
|
||||
// Treasure Trails - hard - https://runescape.wiki/w/Treasure_chest?oldid=848831
|
||||
TrimmedRuneArmour(Items.RUNE_PLATEBODY_T_10800, intArrayOf(Items.RUNE_FULL_HELM_T_2627, Items.RUNE_PLATEBODY_T_2623, Items.RUNE_PLATELEGS_T_2625, Items.RUNE_KITESHIELD_T_2629, Items.RUNE_PLATESKIRT_T_3477), type = StorableFamily.TREASURE_CHEST_HIGH, "Trimmed rune armour"),
|
||||
GoldTrimmedRuneArmour(Items.RUNE_PLATEBODY_G_10798, intArrayOf(Items.RUNE_FULL_HELMG_2619, Items.RUNE_PLATEBODY_G_2615, Items.RUNE_PLATELEGS_G_2617, Items.RUNE_KITESHIELD_G_2621, Items.RUNE_PLATESKIRT_G_3476), type = StorableFamily.TREASURE_CHEST_HIGH, "Gold-trimmed rune armour"),
|
||||
GildedArmour(Items.GILDED_PLATEBODY_10782, intArrayOf(Items.GILDED_FULL_HELM_3486, Items.GILDED_PLATEBODY_3481, Items.GILDED_PLATELEGS_3483, Items.GILDED_KITESHIELD_3488, Items.GILDED_PLATESKIRT_3485), type = StorableFamily.TREASURE_CHEST_HIGH, "Gold-plated rune armour"),
|
||||
RuneHelmH1(Items.RUNE_HELM_H1_10704, intArrayOf(Items.RUNE_HELM_H1_10286), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"),
|
||||
RuneHelmH2(Items.RUNE_HELM_H2_10705, intArrayOf(Items.RUNE_HELM_H2_10288), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"),
|
||||
RuneHelmH3(Items.RUNE_HELM_H3_10706, intArrayOf(Items.RUNE_HELM_H3_10290), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"),
|
||||
RuneHelmH4(Items.RUNE_HELM_H4_10707, intArrayOf(Items.RUNE_HELM_H4_10292), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"),
|
||||
RuneHelmH5(Items.RUNE_HELM_H5_10708, intArrayOf(Items.RUNE_HELM_H5_10294), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"),
|
||||
RuneShieldH1(Items.RUNE_SHIELDH1_10667, intArrayOf(Items.RUNE_SHIELDH1_7336), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"),
|
||||
RuneShieldH2(Items.RUNE_SHIELDH2_10670, intArrayOf(Items.RUNE_SHIELDH2_7342), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"),
|
||||
RuneShieldH3(Items.RUNE_SHIELDH3_10673, intArrayOf(Items.RUNE_SHIELDH3_7348), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"),
|
||||
RuneShieldH4(Items.RUNE_SHIELDH4_10676, intArrayOf(Items.RUNE_SHIELDH4_7354), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"),
|
||||
RuneShieldH5(Items.RUNE_SHIELDH5_10679, intArrayOf(Items.RUNE_SHIELDH5_7360), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"),
|
||||
ZamorakRuneArmour(Items.ZAMORAK_PLATEBODY_10776, intArrayOf(Items.ZAMORAK_FULL_HELM_2657, Items.ZAMORAK_PLATEBODY_2653, Items.ZAMORAK_PLATELEGS_2655, Items.ZAMORAK_KITESHIELD_2659, Items.ZAMORAK_PLATESKIRT_3478), type = StorableFamily.TREASURE_CHEST_HIGH, "Zamorak rune armour"),
|
||||
SaradominRuneArmour(Items.SARADOMIN_PLATE_10778, intArrayOf(Items.SARADOMIN_FULL_HELM_2665, Items.SARADOMIN_PLATEBODY_2661, Items.SARADOMIN_PLATELEGS_2663, Items.SARADOMIN_KITESHIELD_2667, Items.SARADOMIN_PLATESKIRT_3479), type = StorableFamily.TREASURE_CHEST_HIGH, "Saradomin rune armour"),
|
||||
GuthixRuneArmour(Items.GUTHIX_PLATEBODY_10780, intArrayOf(Items.GUTHIX_FULL_HELM_2673, Items.GUTHIX_PLATEBODY_2669, Items.GUTHIX_PLATELEGS_2671, Items.GUTHIX_KITESHIELD_2675, Items.GUTHIX_PLATESKIRT_3480), type = StorableFamily.TREASURE_CHEST_HIGH, "Guthix rune armour"),
|
||||
TrimmedBlueDhide(Items.DHIDE_BODY_T_10685, intArrayOf(Items.DHIDE_BODY_T_7376, Items.DHIDE_CHAPS_T_7384), type = StorableFamily.TREASURE_CHEST_HIGH, "Trimmed blue dragonhide armour"),
|
||||
GoldTrimmedBlueDhide(Items.DHIDE_BODY_G_10684, intArrayOf(Items.DHIDE_BODY_G_7374, Items.DHIDE_CHAPS_G_7382), type = StorableFamily.TREASURE_CHEST_HIGH, "Gold-trimmed blue dragonhide armour"),
|
||||
SaradominBlessedDhide(Items.SARADOMIN_DHIDE_10792, intArrayOf(Items.SARADOMIN_DHIDE_10386, Items.SARADOMIN_CHAPS_10388, Items.SARADOMIN_BRACERS_10384, Items.SARADOMIN_COIF_10390), type = StorableFamily.TREASURE_CHEST_HIGH, "Saradomin blessed dragonhide"),
|
||||
GuthixBlessedDhide(Items.GUTHIX_DRAGONHIDE_10794, intArrayOf(Items.GUTHIX_DRAGONHIDE_10378, Items.GUTHIX_CHAPS_10380, Items.GUTHIX_BRACERS_10376, Items.GUTHIX_COIF_10382), type = StorableFamily.TREASURE_CHEST_HIGH, "Guthix blessed dragonhide"),
|
||||
ZamorakBlessedDhide(Items.ZAMORAK_DHIDE_10790, intArrayOf(Items.ZAMORAK_DHIDE_10370, Items.ZAMORAK_CHAPS_10372, Items.ZAMORAK_BRACERS_10368, Items.ZAMORAK_COIF_10374), type = StorableFamily.TREASURE_CHEST_HIGH, "Zamorak blessed dragonhide"),
|
||||
RobinHoodHat(Items.ROBIN_HOOD_HAT_10796, intArrayOf(Items.ROBIN_HOOD_HAT_2581), type = StorableFamily.TREASURE_CHEST_HIGH),
|
||||
EnchantedRobes(Items.ENCHANTED_TOP_10688, intArrayOf(Items.ENCHANTED_HAT_7400, Items.ENCHANTED_TOP_7399, Items.ENCHANTED_ROBE_7398), type = StorableFamily.TREASURE_CHEST_HIGH, "Enchanted robes"),
|
||||
SaradominVestments(Items.SARADOMIN_ROBE_TOP_10784, intArrayOf(Items.SARADOMIN_MITRE_10452, Items.SARADOMIN_ROBE_TOP_10458, Items.SARADOMIN_ROBE_LEGS_10464, Items.SARADOMIN_STOLE_10470, Items.SARADOMIN_CLOAK_10446, Items.SARADOMIN_CROZIER_10440), type = StorableFamily.TREASURE_CHEST_HIGH, "Saradomin vestments"),
|
||||
ZamorakVestments(Items.ZAMORAK_ROBE_TOP_10786, intArrayOf(Items.ZAMORAK_MITRE_10456, Items.ZAMORAK_ROBE_TOP_10460, Items.ZAMORAK_ROBE_LEGS_10468, Items.ZAMORAK_STOLE_10474, Items.ZAMORAK_CLOAK_10450, Items.ZAMORAK_CROZIER_10444), type = StorableFamily.TREASURE_CHEST_HIGH, "Zamorak vestments"),
|
||||
GuthixVestments(Items.GUTHIX_ROBE_TOP_10788, intArrayOf(Items.GUTHIX_MITRE_10454, Items.GUTHIX_ROBE_TOP_10462, Items.GUTHIX_ROBE_LEGS_10466, Items.GUTHIX_STOLE_10472, Items.GUTHIX_CLOAK_10448, Items.GUTHIX_CROZIER_10442), type = StorableFamily.TREASURE_CHEST_HIGH, "Guthix vestments"),
|
||||
TrimmedAmuletOfGlory(Items.AMULET_OF_GLORYT_10719, intArrayOf(Items.AMULET_OF_GLORYT_10362), type = StorableFamily.TREASURE_CHEST_HIGH, "Trimmed amulet of glory"),
|
||||
PiratesHat(Items.PIRATES_HAT_10774, intArrayOf(Items.PIRATES_HAT_2651), type = StorableFamily.TREASURE_CHEST_HIGH),
|
||||
CavalierMask(Items.CAVALIER_MASK_11277, intArrayOf(Items.CAVALIER_AND_MASK_11280), type = StorableFamily.TREASURE_CHEST_HIGH),
|
||||
DarkCavalier(Items.DARK_CAVALIER_10804, intArrayOf(Items.DARK_CAVALIER_2641), type = StorableFamily.TREASURE_CHEST_HIGH, "Dark brown cavalier"),
|
||||
TanCavalier(Items.TAN_CAVALIER_10802, intArrayOf(Items.TAN_CAVALIER_2639), type = StorableFamily.TREASURE_CHEST_HIGH),
|
||||
BlackCavalier(Items.BLACK_CAVALIER_10806, intArrayOf(Items.BLACK_CAVALIER_2643), type = StorableFamily.TREASURE_CHEST_HIGH),
|
||||
RuneCane(Items.RUNE_CANE_13165, intArrayOf(Items.RUNE_CANE_13099), type = StorableFamily.TREASURE_CHEST_HIGH),
|
||||
TopHat(Items.TOP_HAT_13166, intArrayOf(Items.TOP_HAT_13101), type = StorableFamily.TREASURE_CHEST_HIGH),
|
||||
|
||||
// Magic Wardrobe - https://runescape.wiki/w/Magic_wardrobe?oldid=848846
|
||||
MysticBlueSet(Items.MYSTIC_HAT_10601, intArrayOf(Items.MYSTIC_HAT_4089, Items.MYSTIC_ROBE_TOP_4091, Items.MYSTIC_ROBE_BOTTOM_4093, Items.MYSTIC_GLOVES_4095, Items.MYSTIC_BOOTS_4097), type = StorableFamily.MAGIC_WARDROBE, "Mystic robes"),
|
||||
MysticDarkSet(Items.MYSTIC_HAT_10602, intArrayOf(Items.MYSTIC_HAT_4099, Items.MYSTIC_ROBE_TOP_4101, Items.MYSTIC_ROBE_BOTTOM_4103, Items.MYSTIC_GLOVES_4105, Items.MYSTIC_BOOTS_4107), type = StorableFamily.MAGIC_WARDROBE, "Dark mystic robes"),
|
||||
MysticLightSet(Items.MYSTIC_HAT_10603, intArrayOf(Items.MYSTIC_HAT_4109, Items.MYSTIC_ROBE_TOP_4111, Items.MYSTIC_ROBE_BOTTOM_4113, Items.MYSTIC_GLOVES_4115, Items.MYSTIC_BOOTS_4117), type = StorableFamily.MAGIC_WARDROBE, "Light mystic robes"),
|
||||
SkeletalSet(Items.SKELETAL_HELM_10604, intArrayOf(Items.SKELETAL_HELM_6137, Items.SKELETAL_TOP_6139, Items.SKELETAL_BOTTOMS_6141, Items.SKELETAL_GLOVES_6153, Items.SKELETAL_BOOTS_6147), type = StorableFamily.MAGIC_WARDROBE, "Skeletal"),
|
||||
InfinitySet(Items.INFINITY_TOP_10605, intArrayOf(Items.INFINITY_HAT_6918, Items.INFINITY_TOP_6916, Items.INFINITY_BOTTOMS_6924, Items.INFINITY_GLOVES_6922, Items.INFINITY_BOOTS_6920), type = StorableFamily.MAGIC_WARDROBE, "Infinity robes"),
|
||||
SplitbarkSet(Items.SPLITBARK_HELM_10606, intArrayOf(Items.SPLITBARK_HELM_3385, Items.SPLITBARK_BODY_3387, Items.SPLITBARK_LEGS_3389, Items.SPLITBARK_GAUNTLETS_3391, Items.SPLITBARK_BOOTS_3393), type = StorableFamily.MAGIC_WARDROBE, "Splitbark armour"),
|
||||
GhostlySet(Items.GHOSTLY_BOOTS_10607, intArrayOf(Items.GHOSTLY_ROBE_6107, Items.GHOSTLY_ROBE_6108, Items.GHOSTLY_HOOD_6109, Items.GHOSTLY_GLOVES_6110, Items.GHOSTLY_BOOTS_6106, Items.GHOSTLY_CLOAK_6111), type = StorableFamily.MAGIC_WARDROBE, "Ghostly robes"),
|
||||
MoonclanSet(Items.MOONCLAN_HAT_10608, intArrayOf(Items.MOONCLAN_HELM_9068, Items.MOONCLAN_ARMOUR_9070, Items.MOONCLAN_SKIRT_9071, Items.MOONCLAN_GLOVES_9072, Items.MOONCLAN_BOOTS_9073), type = StorableFamily.MAGIC_WARDROBE, "Moonclan robes"),
|
||||
LunarSet(Items.LUNAR_HELM_10609, intArrayOf(Items.LUNAR_HELM_9096, Items.LUNAR_TORSO_9097, Items.LUNAR_LEGS_9098, Items.LUNAR_GLOVES_9099, Items.LUNAR_BOOTS_9100, Items.LUNAR_CAPE_9101, Items.LUNAR_AMULET_9102, Items.LUNAR_RING_9104), type = StorableFamily.MAGIC_WARDROBE, "Lunar robes"),
|
||||
RunecrafterYellow(Items.RUNECRAFTER_HAT_13656, intArrayOf(Items.RUNECRAFTER_HAT_13615, Items.RUNECRAFTER_ROBE_13614, Items.RUNECRAFTER_SKIRT_13617), type = StorableFamily.MAGIC_WARDROBE, "Runecrafter robes (yellow)"),
|
||||
RunecrafterGreen(Items.RUNECRAFTER_HAT_13657, intArrayOf(Items.RUNECRAFTER_HAT_13620, Items.RUNECRAFTER_ROBE_13619, Items.RUNECRAFTER_SKIRT_13622), type = StorableFamily.MAGIC_WARDROBE, "Runecrafter robes (green)"),
|
||||
RunecrafterBlue(Items.RUNECRAFTER_HAT_13658, intArrayOf(Items.RUNECRAFTER_HAT_13625, Items.RUNECRAFTER_ROBE_13624, Items.RUNECRAFTER_SKIRT_13627), type = StorableFamily.MAGIC_WARDROBE, "Runecrafter robes (blue)"),
|
||||
DagonHaiSet(Items.DAGONHAI_ROBE_TOP_14497, intArrayOf(Items.DAGONHAI_HAT_14499, Items.DAGONHAI_ROBE_TOP_14497, Items.DAGONHAI_ROBE_BOTTOM_14501), type = StorableFamily.MAGIC_WARDROBE, "Dagon'Hai Robes"),
|
||||
|
||||
// Armour Case - https://runescape.wiki/w/Armour_case?oldid=848835
|
||||
DecorativeArmour(Items.DECORATIVE_ARMOUR_10610, intArrayOf(Items.DECORATIVE_ARMOUR_4069, Items.DECORATIVE_SWORD_4068, Items.DECORATIVE_ARMOUR_4070, Items.DECORATIVE_HELM_4071, Items.DECORATIVE_SHIELD_4072, Items.DECORATIVE_ARMOUR_4504, Items.DECORATIVE_SWORD_4503, Items.DECORATIVE_ARMOUR_4505, Items.DECORATIVE_HELM_4506, Items.DECORATIVE_SHIELD_4507, Items.DECORATIVE_ARMOUR_4509, Items.DECORATIVE_SWORD_4508, Items.DECORATIVE_ARMOUR_4510, Items.DECORATIVE_HELM_4511, Items.DECORATIVE_SHIELD_4512), type = StorableFamily.ARMOUR_CASE, "Castlewars armour"), // should be any one colour set per wiki
|
||||
VoidKnightArmour(Items.VOID_KNIGHT_TOP_10611, intArrayOf(Items.VOID_KNIGHT_TOP_8839, Items.VOID_KNIGHT_ROBE_8840, Items.VOID_KNIGHT_GLOVES_8842), type = StorableFamily.ARMOUR_CASE, "Void Knight armour"),
|
||||
RogueArmour(Items.ROGUE_MASK_10612, intArrayOf(Items.ROGUE_MASK_5554, Items.ROGUE_TOP_5553, Items.ROGUE_TROUSERS_5555), type = StorableFamily.ARMOUR_CASE, "Rogue armour"),
|
||||
SpinedArmour(Items.SPINED_HELM_10614, intArrayOf(Items.SPINED_HELM_6131, Items.SPINED_BODY_6133, Items.SPINED_CHAPS_6135, Items.SPINED_BOOTS_6143, Items.SPINED_GLOVES_6149), type = StorableFamily.ARMOUR_CASE, "Spined armour"),
|
||||
RockshellArmour(Items.ROCK_SHELL_HELM_10613, intArrayOf(Items.ROCK_SHELL_HELM_6128, Items.ROCK_SHELL_PLATE_6129, Items.ROCK_SHELL_LEGS_6130, Items.ROCK_SHELL_BOOTS_6145, Items.ROCK_SHELL_GLOVES_6151), type = StorableFamily.ARMOUR_CASE, "Rockshell armour"),
|
||||
TribalMaskP(Items.TRIBAL_MASK_10615, intArrayOf(Items.TRIBAL_MASK_6335), type = StorableFamily.ARMOUR_CASE, "Broodoo mask (poison)"),
|
||||
TribalMaskD(Items.TRIBAL_MASK_10616, intArrayOf(Items.TRIBAL_MASK_6337), type = StorableFamily.ARMOUR_CASE, "Broodoo mask (disease)"),
|
||||
TribalMaskC(Items.TRIBAL_MASK_10617, intArrayOf(Items.TRIBAL_MASK_6339), type = StorableFamily.ARMOUR_CASE, "Broodoo mask (combat)"),
|
||||
WhiteKnightArmour(Items.WHITE_PLATEBODY_10618, intArrayOf(Items.WHITE_PLATEBODY_6617, Items.WHITE_FULL_HELM_6623, Items.WHITE_PLATELEGS_6625, Items.WHITE_PLATESKIRT_6627), type = StorableFamily.ARMOUR_CASE, "White knight armour"),
|
||||
TempleKnightInitiateArmour(Items.INITIATE_HAUBERK_10619, intArrayOf(Items.INITIATE_SALLET_5574, Items.INITIATE_HAUBERK_5575, Items.INITIATE_CUISSE_5576), type = StorableFamily.ARMOUR_CASE, "Temple Knight initiate armour"),
|
||||
TempleKnightProselyteArmour(Items.PROSELYTE_HAUBERK_10620, intArrayOf(Items.PROSELYTE_SALLET_9672, Items.PROSELYTE_HAUBERK_9674, Items.PROSELYTE_CUISSE_9676, Items.PROSELYTE_TASSET_9678), type = StorableFamily.ARMOUR_CASE, "Temple Knight proselyte armour"),
|
||||
MournerGear(Items.MOURNER_TOP_10621, intArrayOf(Items.MOURNER_TOP_6065, Items.MOURNER_TROUSERS_6066, Items.MOURNER_BOOTS_6069, Items.MOURNER_CLOAK_6070), type = StorableFamily.ARMOUR_CASE, "Mourner gear"),
|
||||
GraahkHunterGear(Items.GRAAHK_TOP_10624, intArrayOf(Items.GRAAHK_TOP_10049, Items.GRAAHK_LEGS_10047, Items.GRAAHK_HEADDRESS_10051), type = StorableFamily.ARMOUR_CASE, "Graahk hunter gear"),
|
||||
LarupiaHunterGear(Items.LARUPIA_TOP_10623, intArrayOf(Items.LARUPIA_TOP_10043, Items.LARUPIA_LEGS_10041, Items.LARUPIA_HAT_10045), type = StorableFamily.ARMOUR_CASE, "Larupia hunter gear"),
|
||||
KyattHunterGear(Items.KYATT_TOP_10622, intArrayOf(Items.KYATT_TOP_10037, Items.KYATT_LEGS_10035, Items.KYATT_HAT_10039), type = StorableFamily.ARMOUR_CASE, "Kyatt hunter gear"),
|
||||
PolarCamouflageGear(Items.POLAR_CAMO_TOP_10628, intArrayOf(Items.POLAR_CAMO_TOP_10628, Items.POLAR_CAMO_LEGS_10067), type = StorableFamily.ARMOUR_CASE, "Polar camouflage gear"),
|
||||
JungleCamouflageGear(Items.JUNGLE_CAMO_TOP_10626, intArrayOf(Items.JUNGLE_CAMO_TOP_10057, Items.JUNGLE_CAMO_LEGS_10059), type = StorableFamily.ARMOUR_CASE, "Jungle camouflage gear"),
|
||||
WoodCamouflageGear(Items.WOOD_CAMO_TOP_10625, intArrayOf(Items.WOOD_CAMO_TOP_10053, Items.WOOD_CAMO_LEGS_10055), type = StorableFamily.ARMOUR_CASE, "Wood camouflage gear"),
|
||||
DesertCamouflageGear(Items.DESERT_CAMO_TOP_10627, intArrayOf(Items.DESERT_CAMO_TOP_10061, Items.DESERT_CAMO_LEGS_10063), type = StorableFamily.ARMOUR_CASE, "Desert camouflage gear"),
|
||||
BuildersCostume(Items.BUILDERS_SHIRT_10863, intArrayOf(Items.BUILDERS_SHIRT_10863, Items.BUILDERS_TROUSERS_10864, Items.BUILDERS_BOOTS_10865), type = StorableFamily.ARMOUR_CASE, "Builder's costume"),
|
||||
LumberjackCostume(Items.LUMBERJACK_TOP_10945, intArrayOf(Items.LUMBERJACK_TOP_10939, Items.LUMBERJACK_LEGS_10940, Items.LUMBERJACK_HAT_10941), type = StorableFamily.ARMOUR_CASE, "Lumberjack costume"),
|
||||
BomberJacketCostume(Items.BOMBER_JACKET_11135, intArrayOf(Items.BOMBER_JACKET_9944, Items.BOMBER_CAP_9945), type = StorableFamily.ARMOUR_CASE, "Bomber jacket costume"),
|
||||
HAMRobes(Items.HAM_SHIRT_11274, intArrayOf(Items.HAM_SHIRT_4298, Items.HAM_ROBE_4300, Items.HAM_HOOD_4302, Items.HAM_CLOAK_4304, Items.HAM_LOGO_4306, Items.BOOTS_4310, Items.GLOVES_4308), type = StorableFamily.ARMOUR_CASE, "H.A.M. robes"),
|
||||
VoidMelee(Items.VOID_MELEE_HELM_11676, intArrayOf(Items.VOID_MELEE_HELM_11665), type = StorableFamily.ARMOUR_CASE, "Void melee helm"),
|
||||
VoidRanger(Items.VOID_RANGER_HELM_11675, intArrayOf(Items.VOID_RANGER_HELM_11664), type = StorableFamily.ARMOUR_CASE, "Void ranger helm"),
|
||||
VoidMage(Items.VOID_MAGE_HELM_11674, intArrayOf(Items.VOID_MAGE_HELM_11663), type = StorableFamily.ARMOUR_CASE, "Void mage helm"),
|
||||
BlackNaval(Items.BLACK_NAVAL_SHIRT_13185, intArrayOf(Items.BLACK_NAVAL_SHIRT_8956, Items.BLACK_NAVY_SLACKS_8995, Items.BLACK_TRICORN_HAT_8963),type = StorableFamily.ARMOUR_CASE, "Black naval costume"),
|
||||
RedNaval(Items.RED_NAVAL_SHIRT_13183, intArrayOf(Items.RED_TRICORN_HAT_8961, Items.RED_NAVAL_SHIRT_8954, Items.RED_NAVY_SLACKS_8993), type = StorableFamily.ARMOUR_CASE, "Red naval costume"),
|
||||
BlueNaval(Items.BLUE_NAVAL_SHIRT_13181, intArrayOf(Items.BLUE_TRICORN_HAT_8959, Items.BLUE_NAVAL_SHIRT_8952, Items.BLUE_NAVY_SLACKS_8991), type = StorableFamily.ARMOUR_CASE, "Blue naval costume"),
|
||||
BrownNaval(Items.BROWN_NAVAL_SHIRT_13184, intArrayOf(Items.BROWN_TRICORN_HAT_8962, Items.BROWN_NAVAL_SHIRT_8955, Items.BROWN_NAVY_SLACKS_8994), type = StorableFamily.ARMOUR_CASE, "Brown naval costume"),
|
||||
GreenNaval(Items.GREEN_NAVAL_SHIRT_13182, intArrayOf(Items.GREEN_TRICORN_HAT_8960, Items.GREEN_NAVAL_SHIRT_8953, Items.GREEN_NAVY_SLACKS_8992), type = StorableFamily.ARMOUR_CASE, "Green naval costume"),
|
||||
GreyNaval(Items.GREY_NAVAL_SHIRT_13187, intArrayOf(Items.GREY_TRICORN_HAT_8965, Items.GREY_NAVAL_SHIRT_8958, Items.GREY_NAVY_SLACKS_8997), type = StorableFamily.ARMOUR_CASE, "Grey naval costume"),
|
||||
PurpleNaval(Items.PURPLE_NAVAL_SHIRT_13186, intArrayOf(Items.PURPLE_TRICORN_HAT_8964, Items.PURPLE_NAVAL_SHIRT_8957, Items.PURPLE_NAVY_SLACKS_8996), type = StorableFamily.ARMOUR_CASE, "Purple naval costume"),
|
||||
EliteBlackArmour(Items.ELITE_BLACK_PLATEBODY_14492, intArrayOf(Items.ELITE_BLACK_FULL_HELM_14494, Items.ELITE_BLACK_PLATEBODY_14492, Items.ELITE_BLACK_PLATELEGS_14490), type = StorableFamily.ARMOUR_CASE, "Elite Black Armour")
|
||||
;
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Map of all item ids to their [Storable].
|
||||
*/
|
||||
private val idToStorable: Map<Int, Storable> by lazy {
|
||||
values().flatMap { storable ->
|
||||
storable.takeIds.map { id -> id to storable }
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of all item ids across all [Storable].
|
||||
*/
|
||||
private val allItemIds: Set<Int> by lazy { idToStorable.keys }
|
||||
|
||||
/**
|
||||
* Precomputed set of item ids for each Storable for fast lookup.
|
||||
*/
|
||||
private val storableIdSets: Map<Storable, Set<Int>> by lazy {
|
||||
values().associateWith { it.takeIds.toSet() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all item ids from all storables.
|
||||
*/
|
||||
fun allIds(): Set<Int> = allItemIds
|
||||
|
||||
/**
|
||||
* Finds the storable for the given item id.
|
||||
*/
|
||||
fun fromId(id: Int): Storable? = idToStorable[id]
|
||||
|
||||
/**
|
||||
* Checks if the player owns at least one item from
|
||||
* the storable in equipment or bank.
|
||||
*/
|
||||
fun hasStorable(player: Player, storable: Storable): Boolean {
|
||||
val ownedIds = (player.equipment.toArray() + player.bank.toArray())
|
||||
.mapNotNull { it?.id }
|
||||
.toSet()
|
||||
return storableIdSets[storable]?.any { it in ownedIds } ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player owns any item
|
||||
* from any storable in equipment or bank.
|
||||
*/
|
||||
fun hasAnyStorable(player: Player): Boolean {
|
||||
val ownedIds = (player.equipment.toArray() + player.bank.toArray())
|
||||
.mapNotNull { it?.id }
|
||||
.toSet()
|
||||
return allItemIds.any { it in ownedIds }
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has at least one
|
||||
* item from the given storable equipped.
|
||||
*/
|
||||
fun hasStorableEquipped(player: Player, storable: Storable): Boolean {
|
||||
val equippedIds = player.equipment.toArray().mapNotNull { it?.id }.toSet()
|
||||
return storableIdSets[storable]?.any { it in equippedIds } ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has at least one
|
||||
* item from the given storable in inventory.
|
||||
*/
|
||||
fun hasStorableInInventory(player: Player, storable: Storable): Boolean {
|
||||
val inventoryIds = player.inventory.toArray().mapNotNull { it?.id }.toSet()
|
||||
return storableIdSets[storable]?.any { it in inventoryIds } ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all [Storable]s that the player owns in their bank.
|
||||
*/
|
||||
fun getStorablesInBank(player: Player): List<Storable> {
|
||||
val bankIds = player.bank.toArray().mapNotNull { it?.id }.toSet()
|
||||
return values().filter { storable ->
|
||||
storableIdSets[storable]?.any { it in bankIds } ?: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package content.global.skill.construction.decoration.pohstorage
|
||||
|
||||
/**
|
||||
* Types (families) of storables in Construction's POH Costume Room or Bookcase.
|
||||
*/
|
||||
|
||||
enum class StorableFamily {
|
||||
FANCY_DRESS,
|
||||
ARMOUR_CASE,
|
||||
MAGIC_WARDROBE,
|
||||
TREASURE_CHEST_LOW,
|
||||
TREASURE_CHEST_MED,
|
||||
TREASURE_CHEST_HIGH,
|
||||
BOOKCASE,
|
||||
CAPE_RACK,
|
||||
CAPE_RACK_SKILL,
|
||||
TOY_BOX
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package content.global.skill.construction.decoration.pohstorage
|
||||
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
|
||||
/**
|
||||
* This represents the container where all the POH storable items are.
|
||||
*/
|
||||
|
||||
class StorageContainer {
|
||||
|
||||
// item IDs stored in this container
|
||||
private val stored: MutableSet<Int> = mutableSetOf()
|
||||
|
||||
// page index for the active interface
|
||||
private var currentPage: Int = 0
|
||||
|
||||
// functions to add item, withdraw, check for, etc.
|
||||
fun addItem(id: Int) {
|
||||
stored.add(id)
|
||||
}
|
||||
|
||||
fun withdraw(id: Int): Boolean {
|
||||
return stored.remove(id)
|
||||
}
|
||||
|
||||
fun contains(id: Int): Boolean = id in stored
|
||||
|
||||
fun getItems(): List<Int> = stored.toList()
|
||||
|
||||
// paging
|
||||
fun getPageIndex(): Int = currentPage
|
||||
|
||||
fun nextPage(totalItems: Int, pageSize: Int) {
|
||||
val next = currentPage + 1
|
||||
if (next * pageSize < totalItems) {
|
||||
currentPage = next
|
||||
}
|
||||
}
|
||||
|
||||
fun prevPage() {
|
||||
if (currentPage > 0) {
|
||||
currentPage--
|
||||
}
|
||||
}
|
||||
|
||||
fun resetPage() {
|
||||
currentPage = 0
|
||||
}
|
||||
|
||||
// saving to json and reading back
|
||||
fun toJson(): JSONObject {
|
||||
val json = JSONObject()
|
||||
val items = JSONArray()
|
||||
stored.forEach { items.add(it) }
|
||||
json["items"] = items
|
||||
return json
|
||||
}
|
||||
|
||||
fun readJson(json: JSONObject) {
|
||||
stored.clear()
|
||||
val items = json["items"] as? JSONArray ?: return
|
||||
for (id in items) {
|
||||
stored.add((id as Number).toInt())
|
||||
}
|
||||
currentPage = 0
|
||||
}
|
||||
|
||||
// clear storage
|
||||
fun clear() {
|
||||
stored.clear()
|
||||
currentPage = 0
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
package content.global.skill.construction.decoration.pohstorage
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.InterfaceListener
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.net.packet.PacketRepository
|
||||
import core.net.packet.context.ContainerContext
|
||||
import core.net.packet.out.ContainerPacket
|
||||
import org.rs09.consts.Items
|
||||
|
||||
/**
|
||||
* Handles the interface and interactions for
|
||||
* the Construction Costume Room storage boxes and Bookshelves
|
||||
* https://youtu.be/tAIDQRh4U2g?si=oDaKEJT-ZPs4MoZa&t=261
|
||||
* https://www.youtube.com/watch?v=D2aVfpPCAgY&t=41s - great view of all the treasure chests
|
||||
* https://youtu.be/AK6wVwumvHY?si=XAJsQeSdRlBZSM1f - mid-level costume room
|
||||
* https://www.youtube.com/watch?v=sNEOq_Barlg&t=126s - 2011, but a good video
|
||||
*/
|
||||
class StorageInterface : InterfaceListener {
|
||||
companion object {
|
||||
private const val INTERFACE = 467
|
||||
private const val COMPONENT = 164
|
||||
private const val SIZE = 30
|
||||
private const val PAGE_SIZE = SIZE - 1
|
||||
private const val BUTTON_MORE = Items.MORE_10165
|
||||
private const val BUTTON_BACK = Items.BACK_10166
|
||||
|
||||
/**
|
||||
* Singleton instance of the storage interface.
|
||||
*/
|
||||
lateinit var instance: StorageInterface
|
||||
private set
|
||||
|
||||
/**
|
||||
* Public method to get the storage container for a specific family.
|
||||
*/
|
||||
fun getStorageContainer(player: Player, type: StorableFamily) =
|
||||
player.getPOHStorageState().getContainer(type)
|
||||
|
||||
/**
|
||||
* Opens the storage box.
|
||||
*
|
||||
* @param player The player.
|
||||
* @param type The type of storage to open.
|
||||
* @param box the specific tier of storage we are opening.
|
||||
*/
|
||||
fun openStorage(player: Player, type: StorableFamily, box: StorageOptionPlugin.StorageBox) {
|
||||
instance.openStorageForType(player, type, box)
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the Storable enum entry associated with a given display item ID.
|
||||
*/
|
||||
fun findStorableByDisplayId(id: Int): Storable? =
|
||||
Storable.values().find { it.displayId == id }
|
||||
}
|
||||
|
||||
/**
|
||||
* The attribute of whether the player has read a book or not is stored in a set of two bitfields.
|
||||
* There are 70 books so that's why two longs are needed.
|
||||
*/
|
||||
object BookcaseBitfields {
|
||||
// 0-63
|
||||
const val BOOKCASE_LOW_ATTR = "/save:bookcase_bitfield_low"
|
||||
// 64-127
|
||||
const val BOOKCASE_HIGH_ATTR = "/save:bookcase_bitfield_high"
|
||||
|
||||
data class BookcaseBits(var low: Long, var high: Long)
|
||||
|
||||
// Retrieves both Long bitfields.
|
||||
fun get(player: Player): BookcaseBits {
|
||||
val low = getAttribute(player, BOOKCASE_LOW_ATTR, 0L) as? Long ?: 0L
|
||||
val high = getAttribute(player, BOOKCASE_HIGH_ATTR, 0L) as? Long ?: 0L
|
||||
return BookcaseBits(low, high)
|
||||
}
|
||||
|
||||
// Sets both Long bitfields.
|
||||
fun set(player: Player, bits: BookcaseBits) {
|
||||
setAttribute(player, BOOKCASE_LOW_ATTR, bits.low)
|
||||
setAttribute(player, BOOKCASE_HIGH_ATTR, bits.high)
|
||||
}
|
||||
|
||||
// Checks if a specific bit is set within the two Longs.
|
||||
fun isBitSet(bits: BookcaseBits, bitIndex: Int): Boolean {
|
||||
return if (bitIndex < 64) {
|
||||
(bits.low and (1L shl bitIndex)) != 0L
|
||||
} else {
|
||||
val highIndex = bitIndex - 64
|
||||
(bits.high and (1L shl highIndex)) != 0L
|
||||
}
|
||||
}
|
||||
|
||||
// Sets a specific bit within the two Longs.
|
||||
fun setBit(bits: BookcaseBits, bitIndex: Int): BookcaseBits {
|
||||
if (bitIndex < 64) {
|
||||
bits.low = bits.low or (1L shl bitIndex)
|
||||
} else {
|
||||
val highIndex = bitIndex - 64
|
||||
bits.high = bits.high or (1L shl highIndex)
|
||||
}
|
||||
return bits
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
instance = this
|
||||
}
|
||||
|
||||
override fun defineInterfaceListeners() {
|
||||
on(INTERFACE) { player, _, _, buttonId, _, _ ->
|
||||
val typeName = getAttribute(player, "con:storage:type", null) as? String ?: return@on true
|
||||
val type = StorableFamily.valueOf(typeName.uppercase())
|
||||
handleStorageInteraction(player, buttonId, type)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the StorableFamilies that should be displayed in the interface for a given primary type.
|
||||
* This handles the special case for the Cape Rack because capes are classified as two different families/types
|
||||
* (normal and skillcape) but displayed in the same storage.
|
||||
*/
|
||||
private fun getFamiliesToDisplay(primaryType: StorableFamily): List<StorableFamily> {
|
||||
return when (primaryType) {
|
||||
// if the cape rack can hold skill capes, also show normal capes
|
||||
StorableFamily.CAPE_RACK_SKILL -> listOf(StorableFamily.CAPE_RACK, StorableFamily.CAPE_RACK_SKILL)
|
||||
// else only display the items for the primary family
|
||||
else -> listOf(primaryType)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This handles filtering the list for books.
|
||||
* Only items are shown that have already been read.
|
||||
* If the book is part of a quest, the quest must be complete before it shows up in the POH.
|
||||
* If the index is -1 it is not a book, and not filtered.
|
||||
*/
|
||||
private fun getFilteredItems(player: Player, families: List<StorableFamily>, type: StorableFamily): List<Storable> {
|
||||
// filter for normal items
|
||||
var allItems = Storable.values().filter { it.type in families }
|
||||
|
||||
// filter for bookcase
|
||||
if (type == StorableFamily.BOOKCASE) {
|
||||
val bookcaseBits = BookcaseBitfields.get(player)
|
||||
allItems = allItems.filter { storable ->
|
||||
// check if read
|
||||
val isRead = if (storable.bitIndex == -1) true else BookcaseBitfields.isBitSet(bookcaseBits, storable.bitIndex)
|
||||
// check if quest is done
|
||||
val questMet = storable.questReq == null || isQuestComplete(player, storable.questReq)
|
||||
isRead && questMet
|
||||
}
|
||||
}
|
||||
|
||||
return allItems
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles interaction with storage interface.
|
||||
*/
|
||||
private fun handleStorageInteraction(player: Player, buttonId: Int, type: StorableFamily) {
|
||||
val container = getStorageContainer(player, type)
|
||||
val families = getFamiliesToDisplay(type)
|
||||
val allItems = getFilteredItems(player, families, type)
|
||||
val pageIndex = container.getPageIndex()
|
||||
|
||||
val pageItems = allItems.drop(pageIndex * PAGE_SIZE).take(PAGE_SIZE)
|
||||
val slots = MutableList<Any?>(SIZE) { null }
|
||||
|
||||
var idx = 0
|
||||
pageItems.forEach {
|
||||
if (idx >= SIZE) return@forEach
|
||||
slots[idx++] = it
|
||||
}
|
||||
|
||||
val hasPrev = pageIndex > 0
|
||||
val hasNext = allItems.size > (pageIndex + 1) * PAGE_SIZE
|
||||
|
||||
if (hasNext && idx < SIZE) slots[idx++] = "MORE"
|
||||
if (hasPrev && idx < SIZE) slots[idx] = "BACK"
|
||||
|
||||
val slotIndex = when {
|
||||
buttonId in 56..(56 + (SIZE - 1) * 2) step 2 -> (buttonId - 56) / 2
|
||||
buttonId in 165..223 step 2 -> (buttonId - 165) / 2
|
||||
else -> return
|
||||
}
|
||||
|
||||
when (val clicked = slots.getOrNull(slotIndex)) {
|
||||
"MORE" -> {
|
||||
container.nextPage(allItems.size, PAGE_SIZE)
|
||||
openInterface(player, INTERFACE)
|
||||
updateStorageInterface(player, type)
|
||||
}
|
||||
"BACK" -> {
|
||||
container.prevPage()
|
||||
openInterface(player, INTERFACE)
|
||||
updateStorageInterface(player, type)
|
||||
}
|
||||
is Storable -> processItemTransaction(player, clicked, type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles taking or depositing an item from/to the storage.
|
||||
*/
|
||||
private fun processItemTransaction(player: Player, item: Storable, type: StorableFamily) {
|
||||
// container for the interface/capacity check based on the object the player clicked
|
||||
val interfaceContainer = getStorageContainer(player, type)
|
||||
|
||||
// container for the actual storage/retrieval based on the item's family
|
||||
val targetContainer = getStorageContainer(player, item.type)
|
||||
|
||||
// get stored items from the target container
|
||||
val storedItems = targetContainer.getItems().toSet()
|
||||
|
||||
val box = getAttribute(player, "con:storage:box", null) as? StorageOptionPlugin.StorageBox ?: return
|
||||
val capacity = box.capacity
|
||||
|
||||
// gets the currently stored IDs
|
||||
val storedTakeIds = item.takeIds.filter { it in storedItems }
|
||||
|
||||
if (storedTakeIds.isNotEmpty()) {
|
||||
// withdraw items
|
||||
if (freeSlots(player) < storedTakeIds.size) {
|
||||
sendMessage(player, "You don't have enough inventory space.")
|
||||
return
|
||||
}
|
||||
sendMessage(player, "You take the item from the box.")
|
||||
|
||||
storedTakeIds.forEach { id ->
|
||||
addItem(player, id, 1)
|
||||
targetContainer.withdraw(id)
|
||||
}
|
||||
} else {
|
||||
// deposit items
|
||||
|
||||
// capacity check
|
||||
if (capacity < Int.MAX_VALUE) {
|
||||
|
||||
val familyToCheck = when (type) {
|
||||
// for cape racks, only skillcapes are counted against the capacity
|
||||
StorableFamily.CAPE_RACK_SKILL -> StorableFamily.CAPE_RACK_SKILL
|
||||
// for all other storage, check against the primary family type for capacity
|
||||
else -> type
|
||||
}
|
||||
|
||||
// check if skillcape
|
||||
if (item.type == familyToCheck) {
|
||||
|
||||
val capacityCheckItems = interfaceContainer.getItems().toSet()
|
||||
|
||||
// calculate stored skillcapes
|
||||
val storedSetsCount = Storable.values()
|
||||
.filter { it.type == familyToCheck }
|
||||
.count { storable ->
|
||||
storable.takeIds.any { it in capacityCheckItems }
|
||||
}
|
||||
|
||||
// enforce capacity limits
|
||||
if (storedSetsCount >= capacity) {
|
||||
val message = when (familyToCheck) {
|
||||
StorableFamily.CAPE_RACK_SKILL -> "You cannot store any more skillcapes in this container."
|
||||
else -> "You cannot store any more items in this container."
|
||||
}
|
||||
sendMessage(player, message)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val presentTakeIds = item.takeIds.filter { player.inventory.contains(it, 1) }
|
||||
|
||||
if (presentTakeIds.isEmpty()) {
|
||||
sendMessage(player, "You don't have that item in your inventory.")
|
||||
return
|
||||
}
|
||||
sendMessage(player, "You put the item into the box.")
|
||||
|
||||
presentTakeIds.forEach { id ->
|
||||
removeItem(player, Item(id))
|
||||
targetContainer.addItem(id)
|
||||
}
|
||||
}
|
||||
updateStorageInterface(player, type)
|
||||
}
|
||||
|
||||
private fun updateStorageInterface(player: Player, type: StorableFamily) {
|
||||
val container = getStorageContainer(player, type)
|
||||
val families = getFamiliesToDisplay(type)
|
||||
val allItems = getFilteredItems(player, families, type)
|
||||
val allStoredItems = mutableSetOf<Int>()
|
||||
|
||||
families.forEach { family ->
|
||||
val familyContainer = getStorageContainer(player, family)
|
||||
allStoredItems.addAll(familyContainer.getItems())
|
||||
}
|
||||
|
||||
val stored = allStoredItems.toSet()
|
||||
val pageIndex = container.getPageIndex()
|
||||
|
||||
val pageItems = allItems.drop(pageIndex * PAGE_SIZE).take(PAGE_SIZE)
|
||||
val slots = MutableList<Any?>(SIZE) { null }
|
||||
|
||||
var idx = 0
|
||||
pageItems.forEach {
|
||||
if (idx >= SIZE) return@forEach
|
||||
slots[idx++] = it
|
||||
}
|
||||
|
||||
val hasPrev = pageIndex > 0
|
||||
val hasNext = allItems.size > (pageIndex + 1) * PAGE_SIZE
|
||||
|
||||
if (hasNext && idx < SIZE) slots[idx++] = "MORE"
|
||||
if (hasPrev && idx < SIZE) slots[idx] = "BACK"
|
||||
|
||||
// names each box
|
||||
val title = when {
|
||||
type.name.contains("FANCY_DRESS") -> "Fancy dress box"
|
||||
type.name.contains("TOY_BOX") -> "Toy box"
|
||||
type.name.contains("MAGIC_WARDROBE") -> "Magic wardrobe"
|
||||
type.name.contains("ARMOUR_CASE") -> "Armour case"
|
||||
type.name.contains("BOOKCASE") -> "Bookcase"
|
||||
type.name.contains("CAPE_RACK") -> "Cape rack"
|
||||
type.name.contains("TREASURE_CHEST_LOW") -> "Low-level Treasure Trail rewards"
|
||||
type.name.contains("TREASURE_CHEST_MED") -> "Medium-level Treasure Trail rewards"
|
||||
type.name.contains("TREASURE_CHEST_HIGH") -> "High-level Treasure Trail rewards"
|
||||
|
||||
// to catch any other name
|
||||
else -> type.name.lowercase().replaceFirstChar(Char::titlecase) + " box"
|
||||
}
|
||||
// child 225 is the title block
|
||||
setInterfaceText(player, title, INTERFACE, 225)
|
||||
|
||||
val itemsArray = slots.mapNotNull {
|
||||
when (it) {
|
||||
is Storable -> Item(it.displayId)
|
||||
"MORE" -> Item(BUTTON_MORE)
|
||||
"BACK" -> Item(BUTTON_BACK)
|
||||
else -> null
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
PacketRepository.send(
|
||||
ContainerPacket::class.java,
|
||||
ContainerContext(player, INTERFACE, COMPONENT, SIZE, itemsArray, false)
|
||||
)
|
||||
|
||||
repeat(SIZE) { i ->
|
||||
val nameComponent = 55 + i * 2
|
||||
val iconComponent = 165 + i * 2
|
||||
val hiddenIconComponent = 166 + i * 2
|
||||
|
||||
val obj = slots[i]
|
||||
val (name, isStored) = when (obj) {
|
||||
is Storable -> {
|
||||
val isStored = obj.takeIds.any { it in stored }
|
||||
obj.setName to isStored
|
||||
}
|
||||
"MORE" -> "More..." to true
|
||||
"BACK" -> "Back..." to true
|
||||
else -> "" to false
|
||||
}
|
||||
|
||||
setInterfaceText(player, name, INTERFACE, nameComponent)
|
||||
setComponentVisibility(player, INTERFACE, nameComponent, false)
|
||||
|
||||
if (obj is Storable) {
|
||||
// this displays if the items are stored or not stored
|
||||
setComponentVisibility(player, INTERFACE, iconComponent, !isStored)
|
||||
setComponentVisibility(player, INTERFACE, hiddenIconComponent, isStored)
|
||||
} else {
|
||||
// this is for navigation buttons or empty slots
|
||||
setComponentVisibility(player, INTERFACE, iconComponent, true)
|
||||
setComponentVisibility(player, INTERFACE, hiddenIconComponent, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openStorageForType(player: Player, type: StorableFamily, box: StorageOptionPlugin.StorageBox) {
|
||||
setAttribute(player, "con:storage:type", type.name)
|
||||
setAttribute(player, "con:storage:box", box)
|
||||
openInterface(player, INTERFACE)
|
||||
updateStorageInterface(player, type)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
package content.global.skill.construction.decoration.pohstorage
|
||||
|
||||
import core.api.*
|
||||
import core.cache.def.impl.SceneryDefinition
|
||||
import core.game.interaction.OptionHandler
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.plugin.Initializable
|
||||
import core.plugin.Plugin
|
||||
import org.rs09.consts.Animations
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.Sounds
|
||||
import org.rs09.consts.Scenery as Obj
|
||||
|
||||
/**
|
||||
* Handles interactions with storage objects.
|
||||
*/
|
||||
@Initializable
|
||||
class StorageOptionPlugin : OptionHandler() {
|
||||
|
||||
/**
|
||||
* Represents all storage boxes & configuration.
|
||||
*/
|
||||
enum class StorageBox(val objectIds: IntArray, val storableFamily: StorableFamily? = null, val openable: Boolean = false, val closable: Boolean = false, val capacity: Int = Int.MAX_VALUE) {
|
||||
|
||||
/* Bookcase
|
||||
* Wooden: all
|
||||
* Oak: all
|
||||
* Mahogany: all */
|
||||
BOOKCASE(intArrayOf(Obj.BOOKCASE_13597, Obj.BOOKCASE_13598, Obj.BOOKCASE_13599), StorableFamily.BOOKCASE),
|
||||
|
||||
/* Cape Rack
|
||||
* Oak: normal capes
|
||||
* Teak: 1 skillcape
|
||||
* Mahogany: 5 skillcapes
|
||||
* Gilded: 10 skillcapes
|
||||
* Marble: all
|
||||
* Magic: all */
|
||||
CAPE_RACK(intArrayOf(Obj.OAK_CAPE_RACK_18766), StorableFamily.CAPE_RACK),
|
||||
CAPE_RACK_ONE(intArrayOf(Obj.TEAK_CAPE_RACK_18767), StorableFamily.CAPE_RACK_SKILL, capacity = 1),
|
||||
CAPE_RACK_FIVE(intArrayOf(Obj.MAHOGANY_CAPE_RACK_18768), StorableFamily.CAPE_RACK_SKILL, capacity = 5),
|
||||
CAPE_RACK_TEN(intArrayOf(Obj.GILDED_CAPE_RACK_18769), StorableFamily.CAPE_RACK_SKILL, capacity = 10),
|
||||
CAPE_RACK_ALL(intArrayOf(Obj.MARBLE_CAPE_RACK_18770, Obj.MAGIC_CAPE_RACK_18771), StorableFamily.CAPE_RACK_SKILL),
|
||||
|
||||
/* Fancy Dress Box
|
||||
* Oak: 2
|
||||
* Teak: 4
|
||||
* Mahogany: all */
|
||||
FANCY_BOX_2(intArrayOf(Obj.FANCY_DRESS_BOX_18773), StorableFamily.FANCY_DRESS, false, true, 2),
|
||||
FANCY_BOX_4(intArrayOf(Obj.FANCY_DRESS_BOX_18775), StorableFamily.FANCY_DRESS, false, true, 4),
|
||||
FANCY_BOX_ALL(intArrayOf(Obj.FANCY_DRESS_BOX_18777), StorableFamily.FANCY_DRESS, false, true),
|
||||
FANCY_BOX_OPEN(intArrayOf(Obj.FANCY_DRESS_BOX_18772, Obj.FANCY_DRESS_BOX_18774, Obj.FANCY_DRESS_BOX_18776), openable = true),
|
||||
|
||||
/* Toy Box
|
||||
* Oak: all
|
||||
* Teak: all
|
||||
* Mahogany: all */
|
||||
TOY_BOX_OPEN(intArrayOf(Obj.TOY_BOX_18798, Obj.TOY_BOX_18800, Obj.TOY_BOX_18802), openable = true),
|
||||
TOY_BOX_CLOSE(intArrayOf(Obj.TOY_BOX_18799, Obj.TOY_BOX_18801, Obj.TOY_BOX_18803), closable = true, storableFamily = StorableFamily.TOY_BOX),
|
||||
|
||||
/* Treasure Chest
|
||||
* Oak: Low
|
||||
* Teak: Low, Medium
|
||||
* Mahogany: Low, Medium, High */
|
||||
TREASURE_LOW(intArrayOf(Obj.TREASURE_CHEST_18805), StorableFamily.TREASURE_CHEST_LOW),
|
||||
TREASURE_MED(intArrayOf(Obj.TREASURE_CHEST_18807), StorableFamily.TREASURE_CHEST_MED),
|
||||
TREASURE_HIGH(intArrayOf(Obj.TREASURE_CHEST_18809), StorableFamily.TREASURE_CHEST_HIGH),
|
||||
TREASURE_OPEN(intArrayOf(Obj.TREASURE_CHEST_18804, Obj.TREASURE_CHEST_18806, Obj.TREASURE_CHEST_18808), openable = true),
|
||||
|
||||
/* Magic Wardobe
|
||||
* Oak: 1 set
|
||||
* Carved oak: 2 sets
|
||||
* Teak: 3 sets
|
||||
* Carved Teak: 4 sets
|
||||
* Mahogany: 5 sets
|
||||
* Gilded: 6 sets
|
||||
* Marble: all sets */
|
||||
WARDROBE_1(intArrayOf(Obj.MAGIC_WARDROBE_18785), StorableFamily.MAGIC_WARDROBE, false, true, 1),
|
||||
WARDROBE_2(intArrayOf(Obj.MAGIC_WARDROBE_18787), StorableFamily.MAGIC_WARDROBE, false, true, 2),
|
||||
WARDROBE_3(intArrayOf(Obj.MAGIC_WARDROBE_18789), StorableFamily.MAGIC_WARDROBE, false, true, 3),
|
||||
WARDROBE_4(intArrayOf(Obj.MAGIC_WARDROBE_18791), StorableFamily.MAGIC_WARDROBE, false, true, 4),
|
||||
WARDROBE_5(intArrayOf(Obj.MAGIC_WARDROBE_18793), StorableFamily.MAGIC_WARDROBE, false, true, 5),
|
||||
WARDROBE_6(intArrayOf(Obj.MAGIC_WARDROBE_18795), StorableFamily.MAGIC_WARDROBE, false, true, 6),
|
||||
WARDROBE_ALL(intArrayOf(Obj.MAGIC_WARDROBE_18797), StorableFamily.MAGIC_WARDROBE),
|
||||
WARDROBE_OPEN(intArrayOf(Obj.MAGIC_WARDROBE_18784, Obj.MAGIC_WARDROBE_18786, Obj.MAGIC_WARDROBE_18788, Obj.MAGIC_WARDROBE_18790, Obj.MAGIC_WARDROBE_18792, Obj.MAGIC_WARDROBE_18794, Obj.MAGIC_WARDROBE_18796), openable = true),
|
||||
|
||||
/* Armour Case
|
||||
* Oak: 2
|
||||
* Teak: 4
|
||||
* Mahogany: all */
|
||||
ARMOUR_2(intArrayOf(Obj.ARMOUR_CASE_18779), StorableFamily.ARMOUR_CASE, false, true, 2),
|
||||
ARMOUR_4(intArrayOf(Obj.ARMOUR_CASE_18781), StorableFamily.ARMOUR_CASE, false, true, 4),
|
||||
ARMOUR_ALL(intArrayOf(Obj.ARMOUR_CASE_18783), StorableFamily.ARMOUR_CASE),
|
||||
ARMOUR_OPEN(intArrayOf(Obj.ARMOUR_CASE_18778, Obj.ARMOUR_CASE_18780, Obj.ARMOUR_CASE_18782), openable = true)
|
||||
}
|
||||
|
||||
private val allBoxes = StorageBox.values()
|
||||
|
||||
override fun newInstance(arg: Any?): Plugin<Any> {
|
||||
allBoxes.forEach { box ->
|
||||
box.objectIds.forEach { id ->
|
||||
SceneryDefinition.forId(id)?.let { def ->
|
||||
if (box.storableFamily != null) def.handlers["option:search"] = this
|
||||
if (box.openable) def.handlers["option:open"] = this
|
||||
if (box.closable) def.handlers["option:close"] = this
|
||||
}
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun handle(player: Player, node: Node, option: String): Boolean {
|
||||
val obj = node as Scenery
|
||||
val box = allBoxes.firstOrNull { obj.id in it.objectIds } ?: return true
|
||||
|
||||
when (option) {
|
||||
"search" -> {
|
||||
when (obj.id) {
|
||||
// special handling for med and high level treasure chests since they have a dialogue option first
|
||||
Obj.TREASURE_CHEST_18807 -> {
|
||||
sendDialogueOptions(player, "What sort of Treasure Trail items do you want to see?", "Low-level rewards", "Medium-level rewards")
|
||||
addDialogueAction(player) { p, button ->
|
||||
val family = when (button) {
|
||||
2 -> StorableFamily.TREASURE_CHEST_LOW
|
||||
3 -> StorableFamily.TREASURE_CHEST_MED
|
||||
else -> null
|
||||
}
|
||||
family?.let { StorageInterface.openStorage(p, it, box) }
|
||||
return@addDialogueAction
|
||||
}
|
||||
}
|
||||
Obj.TREASURE_CHEST_18809 -> {
|
||||
sendDialogueOptions(player, "What sort of Treasure Trail items do you want to see?", "Low-level rewards", "Medium-level rewards", "High-level rewards")
|
||||
addDialogueAction(player) { p, button ->
|
||||
val family = when (button) {
|
||||
2 -> StorableFamily.TREASURE_CHEST_LOW
|
||||
3 -> StorableFamily.TREASURE_CHEST_MED
|
||||
4 -> StorableFamily.TREASURE_CHEST_HIGH
|
||||
else -> null
|
||||
}
|
||||
family?.let { StorageInterface.openStorage(p, it, box) }
|
||||
return@addDialogueAction
|
||||
}
|
||||
}
|
||||
// bookcases need to re-store the games book in them on open. I think the bookshelf is the only source.
|
||||
Obj.BOOKCASE_13597 -> {
|
||||
storeBookInHouse(player, Items.GAME_BOOK_7681)
|
||||
box.storableFamily?.let { StorageInterface.openStorage(player, it, box) }
|
||||
}
|
||||
Obj.BOOKCASE_13598 -> {
|
||||
storeBookInHouse(player, Items.GAME_BOOK_7681)
|
||||
box.storableFamily?.let { StorageInterface.openStorage(player, it, box) }
|
||||
}
|
||||
Obj.BOOKCASE_13599 -> {
|
||||
storeBookInHouse(player, Items.GAME_BOOK_7681)
|
||||
box.storableFamily?.let { StorageInterface.openStorage(player, it, box) }
|
||||
}
|
||||
else -> {
|
||||
// default open for rest
|
||||
box.storableFamily?.let { StorageInterface.openStorage(player, it, box) }
|
||||
}
|
||||
}
|
||||
}
|
||||
"open" -> if (box.openable) openBox(player, obj)
|
||||
"close" -> if (box.closable) closeBox(player, obj)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun openBox(player: Player, obj: Scenery) {
|
||||
playAudio(player, Sounds.CHEST_OPEN_52)
|
||||
animate(player, Animations.HUMAN_OPEN_CHEST_536)
|
||||
replaceScenery(obj, obj.id + 1, -1)
|
||||
}
|
||||
|
||||
private fun closeBox(player: Player, obj: Scenery) {
|
||||
playAudio(player, Sounds.CHEST_CLOSE_51)
|
||||
animate(player, 538)
|
||||
replaceScenery(obj, obj.id - 1, -1)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package content.global.skill.construction.decoration.pohstorage
|
||||
|
||||
import org.json.simple.JSONObject
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
class StorageState(val player: Player) {
|
||||
|
||||
private val containers = StorableFamily.values().associateWith { StorageContainer() }.toMutableMap()
|
||||
|
||||
fun getContainer(type: StorableFamily): StorageContainer = containers[type]!!
|
||||
|
||||
fun toJson(): JSONObject {
|
||||
val root = JSONObject()
|
||||
val containerData = JSONObject()
|
||||
|
||||
containers.forEach { (group, container) ->
|
||||
containerData[group.name.lowercase()] = container.toJson()
|
||||
}
|
||||
|
||||
root["containers"] = containerData
|
||||
return root
|
||||
}
|
||||
|
||||
fun readJson(data: JSONObject) {
|
||||
val containerData = data["containers"] as? JSONObject ?: return
|
||||
|
||||
for (group in StorableFamily.values()) {
|
||||
val json = containerData[group.name.lowercase()] as? JSONObject ?: continue
|
||||
containers[group]?.readJson(json)
|
||||
}
|
||||
}
|
||||
|
||||
// clear storage
|
||||
fun clear() {
|
||||
containers.values.forEach { it.clear() }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package content.global.skill.construction.decoration.portalchamber;
|
||||
|
||||
|
||||
import content.data.Quests;
|
||||
import content.global.skill.construction.Hotspot;
|
||||
import content.global.skill.runecrafting.Rune;
|
||||
import core.cache.def.impl.SceneryDefinition;
|
||||
|
|
@ -18,6 +19,7 @@ import core.plugin.Plugin;
|
|||
import core.plugin.ClassScanner;
|
||||
|
||||
import static content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners.ARDOUGNE_TELE_ATTRIBUTE;
|
||||
import static core.api.ContentAPIKt.*;
|
||||
|
||||
/**
|
||||
* PortalChamberPlugin
|
||||
|
|
@ -89,15 +91,14 @@ public class PortalChamberPlugin extends OptionHandler {
|
|||
}
|
||||
for (Locations l : Locations.values()) {
|
||||
if (l.name().contains(identifier)) {
|
||||
if (l == Locations.ARDOUGNE){
|
||||
if (!player.getAttribute(ARDOUGNE_TELE_ATTRIBUTE, false)) {
|
||||
player.sendMessage("You do not have the requirements to direct the portal there");
|
||||
return;
|
||||
}
|
||||
if ((l == Locations.ARDOUGNE && !getAttribute(player, ARDOUGNE_TELE_ATTRIBUTE, false)) ||
|
||||
(l == Locations.KHARYRLL && !isQuestComplete(player, Quests.DESERT_TREASURE))) {
|
||||
sendMessage(player, "You do not have the requirements to direct the portal there.");
|
||||
return;
|
||||
}
|
||||
Item[] runes = l.runes;
|
||||
if (!player.getInventory().containsItems(runes)) {
|
||||
player.sendMessage("You do not have the required runes to build this portal");
|
||||
sendMessage(player, "You do not have the required runes to build this portal.");
|
||||
return;
|
||||
}
|
||||
player.getInventory().remove(runes);
|
||||
|
|
|
|||
|
|
@ -1,53 +1,53 @@
|
|||
package content.global.skill.construction.decoration.questhall
|
||||
|
||||
import content.data.EnchantedJewellery
|
||||
import content.global.handlers.item.EnchantedJewelleryListener.EnchantedJewelleryDialogueFile
|
||||
import core.api.delayScript
|
||||
import core.api.lock
|
||||
import core.api.openDialogue
|
||||
import core.api.playGlobalAudio
|
||||
import core.api.queueScript
|
||||
import core.api.resetAnimator
|
||||
import core.api.stopExecuting
|
||||
import core.api.teleport
|
||||
import core.api.visualize
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.QueueStrength
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.game.world.update.flag.context.Graphics
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.Sounds
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MountedGlory : InteractionListener {
|
||||
|
||||
val MOUNTED_GLORY = 13523
|
||||
|
||||
val TELEPORTS = arrayOf(
|
||||
Location.create(3087, 3495, 0),
|
||||
Location.create(2919, 3175, 0),
|
||||
Location.create(3104, 3249, 0),
|
||||
Location.create(3304, 3124, 0)
|
||||
)
|
||||
val TELEPORTS = EnchantedJewellery.AMULET_OF_GLORY.locations
|
||||
|
||||
override fun defineListeners() {
|
||||
on(MOUNTED_GLORY, IntType.SCENERY, "Edgeville") { player, `object` ->
|
||||
mountedGloryAction(player, `object`, 0)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(MOUNTED_GLORY, IntType.SCENERY, "Karamja") { player, `object` ->
|
||||
mountedGloryAction(player, `object`, 1)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(MOUNTED_GLORY, IntType.SCENERY, "Draynor Village") { player, `object` ->
|
||||
mountedGloryAction(player, `object`, 2)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(MOUNTED_GLORY, IntType.SCENERY, "Al Kharid") { player, `object` ->
|
||||
mountedGloryAction(player, `object`, 3)
|
||||
return@on true
|
||||
}
|
||||
on(MOUNTED_GLORY, IntType.SCENERY, "Rub") { player, _ ->
|
||||
openDialogue(player,EnchantedJewelleryDialogueFile(EnchantedJewellery.AMULET_OF_GLORY, Item(Items.AMULET_OF_GLORY4_1712), false, false))
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
private fun mountedGloryAction(player : Player, `object` : Node, int : Int) {
|
||||
if (player.houseManager.isBuildingMode) {
|
||||
|
|
@ -57,21 +57,16 @@ class MountedGlory : InteractionListener {
|
|||
if (!player.zoneMonitor.teleport(1, Item(Items.AMULET_OF_GLORY_1704))) {
|
||||
return
|
||||
}
|
||||
Executors.newSingleThreadScheduledExecutor().schedule({
|
||||
player.pulseManager.run(object : Pulse() {
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when (counter++) {
|
||||
1 -> {
|
||||
player.lock(5)
|
||||
player.visualize(Animation(714), Graphics(308, 100, 50))
|
||||
playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200)
|
||||
}
|
||||
4 -> player.animator.reset().also { teleport(player, TELEPORTS[int]) }
|
||||
}
|
||||
return false
|
||||
queueScript(player, 0, QueueStrength.SOFT) { stage ->
|
||||
if (stage == 0) {
|
||||
lock(player, 4)
|
||||
visualize(player, Animation(714), Graphics(308, 100, 50))
|
||||
playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200)
|
||||
return@queueScript delayScript(player, 3)
|
||||
}
|
||||
})
|
||||
}, 0, TimeUnit.SECONDS)
|
||||
resetAnimator(player)
|
||||
teleport(player, TELEPORTS[int])
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
package content.global.skill.construction.decoration.questhall;
|
||||
|
||||
|
||||
import core.cache.def.impl.SceneryDefinition;
|
||||
import core.game.dialogue.DialogueAction;
|
||||
import core.game.dialogue.DialogueInterpreter;
|
||||
import core.game.interaction.OptionHandler;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.system.task.Pulse;
|
||||
import core.game.world.GameWorld;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.game.world.update.flag.context.Graphics;
|
||||
import core.plugin.Initializable;
|
||||
import core.plugin.Plugin;
|
||||
import org.rs09.consts.Sounds;
|
||||
|
||||
import static core.api.ContentAPIKt.playGlobalAudio;
|
||||
|
||||
/**
|
||||
* Handles the POH Mounted Glory.
|
||||
* @author Splinter
|
||||
*/
|
||||
@Initializable
|
||||
public class MountedGloryPlugin extends OptionHandler {
|
||||
|
||||
/**
|
||||
* Represents the teleport animation.
|
||||
*/
|
||||
private static final Animation ANIMATION = new Animation(714);
|
||||
|
||||
/**
|
||||
* Represents the graphics to use.
|
||||
*/
|
||||
private static final Graphics GRAPHICS = new Graphics(308, 100, 50);
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
SceneryDefinition.forId(13523).getHandlers().put("option:rub", this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Player player, Node node, String option) {
|
||||
DialogueInterpreter interpreter = player.getDialogueInterpreter();
|
||||
interpreter.sendOptions("Select a location.", "Edgeville", "Karamja", "Draynor Village", "Al-Kharid", "Nowhere.");
|
||||
interpreter.addAction(new DialogueAction() {
|
||||
|
||||
@Override
|
||||
public void handle(Player player, int buttonId) {
|
||||
switch (buttonId) {
|
||||
case 2:
|
||||
teleport(player, Location.create(3087, 3495, 0));
|
||||
break;
|
||||
case 3:
|
||||
teleport(player, Location.create(2919, 3175, 0));
|
||||
break;
|
||||
case 4:
|
||||
teleport(player, Location.create(3081, 3250, 0));
|
||||
break;
|
||||
case 5:
|
||||
teleport(player, Location.create(3304, 3124, 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to teleport to a location.
|
||||
* @param player the player.
|
||||
* @param location the location.
|
||||
*/
|
||||
private boolean teleport(final Player player, final Location location) {
|
||||
if (player.isTeleBlocked()) {
|
||||
player.sendMessage("A magical force has stopped you from teleporting.");
|
||||
return false;
|
||||
}
|
||||
player.lock();
|
||||
player.visualize(ANIMATION, GRAPHICS);
|
||||
playGlobalAudio(player.getLocation(), Sounds.TELEPORT_ALL_200);
|
||||
player.getImpactHandler().setDisabledTicks(4);
|
||||
GameWorld.getPulser().submit(new Pulse(4, player) {
|
||||
@Override
|
||||
public boolean pulse() {
|
||||
player.unlock();
|
||||
player.getProperties().setTeleportLocation(location);
|
||||
player.getAnimator().reset();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -94,6 +94,7 @@ public enum CookableItems {
|
|||
RAW_OOMLIE(Items.RAW_OOMLIE_2337, 0, Items.BURNT_OOMLIE_2426, 50, 0, 0, 0, 0, 0), // always burns
|
||||
OOMLIE_WRAP(Items.COOKED_OOMLIE_WRAP_2343, Items.WRAPPED_OOMLIE_2341, Items.BURNT_OOMLIE_WRAP_2345, 50, 30, 106, 450, 112, 476),
|
||||
SEAWEED(Items.SEAWEED_401, 0, Items.SODA_ASH_1781, 0, 0, 0, 0, 0, 0),
|
||||
BARLEY(Items.BARLEY_6006, 0, Items.BARLEY_MALT_6008, 0, 0, 0, 0, 0, 0),
|
||||
|
||||
/**
|
||||
* Sinew gets overridden by BEEF in this enum, due to values being looked up by the items RAW id.
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class CookingRewrite : InteractionListener {
|
|||
list.add(Items.COOKED_MEAT_2142)
|
||||
list.add(RAW_BEEF_2132)
|
||||
list.add(RAW_BEAR_MEAT_2136)
|
||||
list.add(Items.BARLEY_6006)
|
||||
list.add(SEAWEED_401)
|
||||
list.add(Items.BOWL_OF_WATER_1921)
|
||||
RAW_FOODS = list.toIntArray()
|
||||
|
|
@ -76,7 +77,7 @@ class CookingRewrite : InteractionListener {
|
|||
player.dialogueInterpreter.open(CookingDialogue(item.id,9436,true,obj,item.id))
|
||||
return@onUseWith true
|
||||
}
|
||||
BREAD_DOUGH_2307, UNCOOKED_CAKE_1889 -> if (!range) {
|
||||
BREAD_DOUGH_2307, UNCOOKED_CAKE_1889, Items.BARLEY_6006 -> if (!range) {
|
||||
player.packetDispatch.sendMessage("You need to cook this on a range.")
|
||||
return@onUseWith false
|
||||
}
|
||||
|
|
@ -104,7 +105,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, 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) {
|
||||
|
|
|
|||
|
|
@ -200,6 +200,9 @@ public class StandardCookingPulse extends Pulse {
|
|||
if (product.getId() == Items.SODA_ASH_1781) {
|
||||
return "You burn the seaweed into soda ash.";
|
||||
}
|
||||
if (product.getId() == Items.BARLEY_MALT_6008) {
|
||||
return null; // Both live branches of Jagexscape lack a message for this and I can't find period evidence of a message
|
||||
}
|
||||
if (CookableItems.intentionalBurn(food.getId())) {
|
||||
return "You deliberately burn the perfectly good piece of meat.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,187 @@
|
|||
package content.global.skill.cooking.fermenting
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.QueueStrength
|
||||
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.game.node.scenery.Scenery
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.tools.ticksToCycles
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.Scenery as SceneryIds
|
||||
|
||||
|
||||
class AppleMushListener : InteractionListener {
|
||||
|
||||
private data class BarrelLocations(
|
||||
val start: Location, // Where the player begins the mushing animation from
|
||||
val jump: Location, // Where the player ends the mushing animation at
|
||||
val tap: Location, // Where the apple barrel's tap is located
|
||||
val stand: Location // Where the player stands to collect from the tap
|
||||
)
|
||||
|
||||
private val barrelLocations = mapOf(
|
||||
14747 to BarrelLocations( // Port Phasmatys
|
||||
start = Location.create(3676, 9959, 0),
|
||||
jump = Location.create(3676, 9957, 0),
|
||||
tap = Location.create(3677, 9959, 0),
|
||||
stand = Location.create(3678, 9959, 0)
|
||||
),
|
||||
11679 to BarrelLocations( // Keldagrim
|
||||
start = Location.create(2914, 10193, 1),
|
||||
jump = Location.create(2914, 10191, 1),
|
||||
tap = Location.create(2915, 10193, 1),
|
||||
stand = Location.create(2916, 10193, 1)
|
||||
),
|
||||
9780 to BarrelLocations( // Gnome Orchard
|
||||
start = Location.create(2484, 3375, 0),
|
||||
jump = Location.create(2484, 3373, 0),
|
||||
tap = Location.create(2485, 3375, 0),
|
||||
stand = Location.create(2486, 3375, 0)
|
||||
)
|
||||
)
|
||||
|
||||
private val emptyAppleBarrels = intArrayOf(
|
||||
SceneryIds.APPLE_BARREL_7403,
|
||||
SceneryIds.APPLE_BARREL_8807
|
||||
)
|
||||
|
||||
private val fullAppleBarrels = intArrayOf(
|
||||
SceneryIds.APPLE_BARREL_7404,
|
||||
SceneryIds.APPLE_BARREL_8808
|
||||
)
|
||||
|
||||
private val appleTaps = intArrayOf(
|
||||
SceneryIds.BARREL_TAP_7406,
|
||||
SceneryIds.BARREL_TAP_8737
|
||||
)
|
||||
|
||||
private val MUSH_ANIM = Animation(2306)
|
||||
|
||||
override fun defineListeners() {
|
||||
|
||||
onUseWith(SCENERY, Items.COOKING_APPLE_1955, *emptyAppleBarrels) { player, _, with ->
|
||||
if (getDynLevel(player, Skills.COOKING) < 14) {
|
||||
sendDialogue(player, "You need a Cooking level of at least 14 in order to do this.")
|
||||
return@onUseWith true
|
||||
}
|
||||
if (!inInventory(player, Items.COOKING_APPLE_1955, 4)) {
|
||||
sendDialogue(player, "You need 4 cooking apples to make apple mush.")
|
||||
return@onUseWith true
|
||||
}
|
||||
if (!inInventory(player, Items.BUCKET_1925)) {
|
||||
sendDialogue(player, "You need a bucket to collect the apple mush.")
|
||||
return@onUseWith true
|
||||
}
|
||||
if (inInventory(player, Items.COOKING_APPLE_1955, 4)) {
|
||||
startMushing(player, with.asScenery())
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
onUseWith(SCENERY, Items.BUCKET_1925, *appleTaps) { player, _, _ ->
|
||||
if (getAttribute(player, "/save:cooking:brewing-apples", false)) {
|
||||
if (removeItem(player, Item(Items.BUCKET_1925, 1))) {
|
||||
addItem(player, Items.APPLE_MUSH_5992, 1)
|
||||
sendMessage(player, "You fill a bucket with apple mush.")
|
||||
}
|
||||
removeAttribute(player, "/save:cooking:brewing-apples")
|
||||
return@onUseWith true
|
||||
}
|
||||
sendMessage(player, "Nothing interesting happens.")
|
||||
return@onUseWith false
|
||||
}
|
||||
}
|
||||
|
||||
private fun startMushing(player: Player, barrel: Scenery) {
|
||||
val initLoc = player.location
|
||||
val barrelLoc = barrel.location
|
||||
val locations = barrelLocations[player.location.regionId]
|
||||
val startLoc = locations?.start
|
||||
val jumpLoc = locations?.jump
|
||||
val standLoc = locations?.stand
|
||||
val tapLoc = locations?.tap
|
||||
val fastStart: Boolean = initLoc == startLoc
|
||||
|
||||
// https://www.youtube.com/watch?v=7I_Ec_tQ-tc
|
||||
// Has to be soft or else you can log out and clog the barrel
|
||||
queueScript(player, 1 ,QueueStrength.SOFT) { counter ->
|
||||
when (counter) {
|
||||
0 -> {
|
||||
stopWalk(player)
|
||||
forceWalk(player, startLoc!!, "smart")
|
||||
lock(player, MUSH_ANIM.duration + 7)
|
||||
return@queueScript delayScript(player, if (fastStart) 1 else 4)
|
||||
}
|
||||
|
||||
1 -> {
|
||||
face(player, barrelLoc)
|
||||
if (!removeItem(player, Item(Items.COOKING_APPLE_1955, 4))) { return@queueScript stopExecuting(player) }
|
||||
setAttribute(player, "/save:cooking:brewing-apples", true)
|
||||
return@queueScript delayScript(player, 0)
|
||||
}
|
||||
|
||||
2 -> {
|
||||
forceMove(player, startLoc!!, barrelLoc, 0, ticksToCycles(1), anim = MUSH_ANIM.id)
|
||||
if (barrel.id == SceneryIds.APPLE_BARREL_7403) sendGraphics(414, barrelLoc)
|
||||
else sendGraphics(413, barrelLoc)
|
||||
swapBarrel(barrel)
|
||||
return@queueScript delayScript(player, MUSH_ANIM.duration - 1)
|
||||
}
|
||||
|
||||
3 -> {
|
||||
forceMove(player, barrelLoc, jumpLoc!!, 0, ticksToCycles(1))
|
||||
return@queueScript delayScript(player, 2)
|
||||
}
|
||||
|
||||
4 -> {
|
||||
stopWalk(player)
|
||||
return@queueScript delayScript(player, 1)
|
||||
}
|
||||
|
||||
5 -> {
|
||||
unlock(player)
|
||||
stopWalk(player)
|
||||
forceWalk(player, standLoc!!, "smart")
|
||||
lock(player, 5)
|
||||
return@queueScript delayScript(player, 4)
|
||||
}
|
||||
|
||||
6 -> {
|
||||
stopWalk(player)
|
||||
face(player, tapLoc!!)
|
||||
return@queueScript delayScript(player, 1)
|
||||
}
|
||||
|
||||
7 -> {
|
||||
if (removeItem(player, Item(Items.BUCKET_1925, 1))) {
|
||||
addItem(player, Items.APPLE_MUSH_5992, 1)
|
||||
sendMessage(player, "You fill a bucket with apple mush.")
|
||||
}
|
||||
removeAttribute(player, "/save:cooking:brewing-apples")
|
||||
val currentBarrel = getScenery(barrelLoc)
|
||||
currentBarrel?.let { swapBarrel(it) }
|
||||
unlock(player)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
else -> return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
unlock(player)
|
||||
}
|
||||
|
||||
private fun swapBarrel(barrel: Scenery) {
|
||||
val emptyIndex = emptyAppleBarrels.indexOf(barrel.id)
|
||||
val fullIndex = fullAppleBarrels.indexOf(barrel.id)
|
||||
val newId = when {
|
||||
emptyIndex != -1 -> fullAppleBarrels[emptyIndex]
|
||||
fullIndex != -1 -> emptyAppleBarrels[fullIndex]
|
||||
else -> return
|
||||
}
|
||||
replaceScenery(barrel, newId, -1)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package content.global.skill.cooking.fermenting
|
||||
|
||||
import core.api.log
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.system.timer.PersistTimer
|
||||
import core.tools.Log
|
||||
import core.tools.secondsToTicks
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
|
||||
/**
|
||||
* The timer for regular brewing operations
|
||||
*/
|
||||
|
||||
const val brewCycleTime = 6 /*hours*/ * 60 /*minutes per hour*/ * 100 /*ticks per minute*/
|
||||
|
||||
class BrewGrowth : PersistTimer(brewCycleTime, "cooking:brewing", isSoft = true) {
|
||||
private val vatMap = HashMap<Int, Vat>()
|
||||
lateinit var player: Player
|
||||
|
||||
override fun onRegister(entity: Entity) {
|
||||
player = (entity as? Player)!!
|
||||
for (vat in vatMap.values.toList()){
|
||||
catchUp(vat.nextBrew)
|
||||
}
|
||||
}
|
||||
|
||||
override fun save(root: JSONObject, entity: Entity) {
|
||||
player = (entity as? Player)!!
|
||||
val vats = JSONArray()
|
||||
for ((key, vat) in vatMap.entries.toList()){
|
||||
val isBrewingStage = vat.stage in listOf(
|
||||
BrewingStage.MIXED,
|
||||
BrewingStage.BREWING1,
|
||||
BrewingStage.BREWING2,
|
||||
BrewingStage.DONE,
|
||||
BrewingStage.MATURE,
|
||||
BrewingStage.BAD
|
||||
)
|
||||
|
||||
if (isBrewingStage) {
|
||||
val v = JSONObject()
|
||||
v["brew-ordinal"] = key
|
||||
v["brew-stuff"] = vat.theStuff
|
||||
v["brewing-nextBrew"] = vat.nextBrew
|
||||
v["brewing-stage"] = vat.stage.ordinal
|
||||
v["brewing-item"] = vat.brewingItem?.ordinal
|
||||
vats.add(v)
|
||||
}
|
||||
}
|
||||
root["brewing"] = vats
|
||||
}
|
||||
|
||||
override fun parse(root: JSONObject, entity: Entity) {
|
||||
player = (entity as? Player)!!
|
||||
val data = root["brewing"] as JSONArray
|
||||
for (d in data){
|
||||
val v = d as JSONObject
|
||||
val vatOrdinal = v["brew-ordinal"].toString().toInt()
|
||||
val vatStuff = v["brew-stuff"].toString().toBoolean()
|
||||
val vatNextBrew = v["brewing-nextBrew"].toString().toLong()
|
||||
val vatStage = BrewingStage.values()[v["brewing-stage"].toString().toInt()]
|
||||
val vatItem = v["brewing-item"]?.toString()?.toIntOrNull()?.let {
|
||||
Brewable.values().getOrNull(it)
|
||||
}
|
||||
|
||||
if (vatMap[vatOrdinal] == null){
|
||||
val brewingVat = BrewingVat.values()[vatOrdinal]
|
||||
val vat = Vat(player, brewingVat, vatStuff, vatNextBrew, vatStage, vatItem)
|
||||
vatMap[vatOrdinal] = vat
|
||||
vat.updateVat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getVat(brewingVat: BrewingVat, addVat: Boolean = true) : Vat {
|
||||
return vatMap[brewingVat.ordinal] ?: (Vat(player, brewingVat).also { if (addVat) vatMap[brewingVat.ordinal] = it })
|
||||
}
|
||||
|
||||
private fun catchUp(timeTillGrow: Long){
|
||||
if (timeTillGrow < System.currentTimeMillis()){
|
||||
val seconds = (System.currentTimeMillis() - timeTillGrow)/1000
|
||||
if (seconds > Int.MAX_VALUE){
|
||||
execute(1024)
|
||||
}
|
||||
else{
|
||||
val cyclesToGrow = secondsToTicks(seconds.toInt()) / brewCycleTime
|
||||
execute(cyclesToGrow)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun run(entity: Entity): Boolean {
|
||||
player = (entity as? Player)!!
|
||||
return execute(0)
|
||||
}
|
||||
|
||||
private fun execute(count: Int) : Boolean {
|
||||
for (vat in vatMap.entries.toList()){
|
||||
if (vat.value.canBrew())
|
||||
for (i in 0..count){
|
||||
val resp = vat.value.brew()
|
||||
if (!resp){
|
||||
break
|
||||
}
|
||||
}
|
||||
else {
|
||||
vatMap.entries.removeIf { it.value.isVatEmpty() && it.value.isBarrelEmpty()}
|
||||
}
|
||||
}
|
||||
return vatMap.isNotEmpty()
|
||||
}
|
||||
|
||||
fun getVats(): MutableCollection<Vat>{
|
||||
return vatMap.values
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package content.global.skill.cooking.fermenting
|
||||
|
||||
import org.rs09.consts.Items
|
||||
|
||||
|
||||
enum class Brewable (val displayName: String, val itemID: Int, val level: Int, val product: IntArray, val levelXP: Double, val vatVarBitOffset: Int,
|
||||
val barrelVarBitOffset: Int, val ingredientAmount: Int = 4){
|
||||
DWARVEN_STOUT("Dwarven Stout", Items.HAMMERSTONE_HOPS_5994, 19, intArrayOf(Items.DWARVEN_STOUT_1913, Items.DWARVEN_STOUT4_5777, Items.DWARVEN_STOUTM_5747, Items.DWARVEN_STOUTM4_5857), 215.0, 4, 8),
|
||||
ASGARNIAN_ALE("Asgarnian Ale", Items.ASGARNIAN_HOPS_5996, 26, intArrayOf(Items.ASGARNIAN_ALE_1905, Items.ASGARNIAN_ALE4_5785, Items.ASGARNIAN_ALEM_5739, Items.ASGARNIAN_ALEM4_5865), 248.0, 10, 16),
|
||||
GREENMANS_ALE("Greenman's Ale", Items.CLEAN_HARRALANDER_255, 29, intArrayOf(Items.GREENMANS_ALE_1909, Items.GREENMANS_ALE4_5793, Items.GREENMANS_ALEM_5743, Items.GREENMANS_ALEM4_5873), 281.0, 16, 24),
|
||||
WIZARD_BOMB("Wizard's Mind Bomb", Items.YANILLIAN_HOPS_5998, 34, intArrayOf(Items.WIZARDS_MIND_BOMB_1907, Items.MIND_BOMB4_5801, Items.MATURE_WMB_5741, Items.MIND_BOMBM4_5881), 341.0, 22, 32),
|
||||
DRAGON_BITTER("Dragon Bitter", Items.KRANDORIAN_HOPS_6000, 39, intArrayOf(Items.DRAGON_BITTER_1911, Items.DRAGON_BITTER4_5809, Items.DRAGON_BITTERM_5745, Items.DRAGON_BITTERM4_5889), 347.0, 28, 40),
|
||||
MOONLIGHT_MEAD("Moonlight Mead", Items.MUSHROOM_6004, 44, intArrayOf(Items.MOONLIGHT_MEAD_2955, Items.MOONLIGHT_MEAD4_5817, Items.MOONLIGHT_MEADM_5749, Items.MLIGHT_MEADM4_5897), 380.0, 34, 48),
|
||||
AXEMANS_FOLLY("Axeman's Folly", Items.OAK_ROOTS_6043, 49, intArrayOf(Items.AXEMANS_FOLLY_5751, Items.AXEMANS_FOLLY4_5825, Items.AXEMANS_FOLLYM_5753, Items.AXEMANS_FOLLYM4_5905), 413.0, 40, 56, 1),
|
||||
CHEFS_DELIGHT("Chef's Delight", Items.CHOCOLATE_DUST_1975, 54, intArrayOf(Items.CHEFS_DELIGHT_5755, Items.CHEFS_DELIGHT4_5833, Items.CHEFS_DELIGHTM_5757, Items.CHEFS_DELIGHTM4_5913), 446.0, 46, 64),
|
||||
SLAYERS_RESPITE("Slayer's Respite", Items.WILDBLOOD_HOPS_6002, 59, intArrayOf(Items.SLAYERS_RESPITE_5759, Items.SLAYERS_RESPITE4_5841, Items.SLAYERS_RESPITEM_5761, Items.SLAYER_RESPITEM4_5921), 479.0, 52, 72),
|
||||
CIDER("cider", Items.APPLE_MUSH_5992, 14, intArrayOf(Items.CIDER_5763, Items.CIDER4_5849, Items.MATURE_CIDER_5765, Items.CIDERM4_5929), 182.0, 58, 80),
|
||||
KELDA_STOUT("Kelda Stout", Items.KELDA_HOPS_6113, 22, intArrayOf(Items.KELDA_STOUT_6118), 0.0, 68, 3, 1);
|
||||
|
||||
|
||||
companion object {
|
||||
fun getIngredients() : IntArray{
|
||||
return values().map { it.itemID }.toIntArray()
|
||||
}
|
||||
|
||||
fun getBrewable(itemId: Int) : Brewable?{
|
||||
return values().find { it.itemID == itemId }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
package content.global.skill.cooking.fermenting
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.QueueStrength
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.Scenery
|
||||
|
||||
|
||||
const val KELDAGRIM_LOC = 11679
|
||||
const val PORT_PHASMATYS_LOC = 14747
|
||||
|
||||
val varbits = mapOf(
|
||||
PORT_PHASMATYS_LOC to BrewingVat.PORT_PHAS,
|
||||
KELDAGRIM_LOC to BrewingVat.KELDAGRIM
|
||||
)
|
||||
|
||||
class BrewingListener : InteractionListener {
|
||||
|
||||
private val aleIngredientsId = Brewable.getIngredients()
|
||||
|
||||
private val vatsNeedingYeast = intArrayOf(
|
||||
Scenery.FERMENTING_VAT_7444, // Hammerstone hops
|
||||
Scenery.FERMENTING_VAT_7449, // Asgarnian hops
|
||||
Scenery.FERMENTING_VAT_7454, // Harralander
|
||||
Scenery.FERMENTING_VAT_7459, // Yanillian hops
|
||||
Scenery.FERMENTING_VAT_7464, // Krandorian hops
|
||||
Scenery.FERMENTING_VAT_7469, // Bittercap mushrooms
|
||||
Scenery.FERMENTING_VAT_7474, // Oak roots
|
||||
Scenery.FERMENTING_VAT_7479, // Chocolate dust
|
||||
Scenery.FERMENTING_VAT_7484, // Wildblood hops
|
||||
Scenery.FERMENTING_VAT_7489, // Apple mush
|
||||
Scenery.FERMENTING_VAT_8871 // Kelda hops
|
||||
)
|
||||
|
||||
// Const Lib is missing some of these
|
||||
private val drainBarrels = intArrayOf(
|
||||
Scenery.BARREL_7408,
|
||||
Scenery.BARREL_7409,
|
||||
Scenery.BARREL_7410,
|
||||
|
||||
)
|
||||
private val barrels = intArrayOf(
|
||||
Scenery.KELDA_STOUT_8870,
|
||||
Scenery.DWARVEN_STOUT_7411,
|
||||
Scenery.ASGARNIAN_ALE_7413,
|
||||
Scenery.GREENMANS_ALE_7415,
|
||||
Scenery.WIZARDS_MIND_BOMB_7417,
|
||||
Scenery.DRAGON_BITTER_7419,
|
||||
Scenery.MOONLIGHT_MEAD_7421,
|
||||
Scenery.AXEMAN_S_FOLLY_7423,
|
||||
Scenery.CHEF_S_DELIGHT_7425,
|
||||
Scenery.SLAYER_S_RESPITE_7427,
|
||||
Scenery.CIDER_7429,
|
||||
Scenery.MATURE_DWARVEN_STOUT_7412,
|
||||
Scenery.MATURE_ASGARNIAN_ALE_7414,
|
||||
Scenery.MATURE_GREENMANS_ALE_7416,
|
||||
Scenery.MATURE_WIZARDS_MIND_BOMB_7418,
|
||||
Scenery.MATURE_DRAGON_BITTER_7420,
|
||||
Scenery.MATURE_MOONLIGHT_MEAD_7422,
|
||||
Scenery.MATURE_AXEMAN_S_FOLLY_7424,
|
||||
Scenery.MATURE_CHEF_S_DELIGHT_7426,
|
||||
Scenery.MATURE_SLAYER_S_RESPITE_7428,
|
||||
Scenery.MATURE_CIDER_7430
|
||||
)
|
||||
|
||||
|
||||
override fun defineListeners() {
|
||||
|
||||
// Fill an empty vat with water - self-contained operation
|
||||
onUseWith(SCENERY, Items.BUCKET_OF_WATER_1929, Scenery.FERMENTING_VAT_7437) { player, _, _ ->
|
||||
if (removeItem(player, Item(Items.BUCKET_OF_WATER_1929, 2))) {
|
||||
queueScript(player, 0) {
|
||||
addItem(player, Items.BUCKET_1925, 2)
|
||||
|
||||
lock(player, Animation(2283).duration)
|
||||
animate(player, Animation(2283))
|
||||
|
||||
sendMessage(player, "You add some water to the vat.")
|
||||
|
||||
val vatVarbit = varbits.getValue(player.location.regionId).varbit
|
||||
setVarbit(player, vatVarbit, 1, true)
|
||||
return@queueScript (stopExecuting(player))
|
||||
}
|
||||
}
|
||||
else{
|
||||
sendDialogue(player, "You need 2 buckets of water.")
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Fill an empty vat with apple mush
|
||||
onUseWith(SCENERY, Items.APPLE_MUSH_5992, Scenery.FERMENTING_VAT_7437) { player, used, _ ->
|
||||
getVat(player).addIngredient(used as Item)
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Add barley malt to a vat with water - self-contained operation
|
||||
onUseWith(SCENERY, Items.BARLEY_MALT_6008, Scenery.FERMENTING_VAT_7438) { player, _, _ ->
|
||||
if (inInventory(player, Items.BARLEY_MALT_6008, 2)) {
|
||||
queueScript(player, 0, QueueStrength.SOFT) { counter ->
|
||||
when (counter) {
|
||||
0 -> {
|
||||
if (!removeItem(player, Item(Items.BARLEY_MALT_6008, 1))) { return@queueScript stopExecuting(player) }
|
||||
|
||||
lock(player, Animation(2295).duration * 2 + 2)
|
||||
animate(player, Animation(2295))
|
||||
|
||||
sendMessage(player, "You add some barley malt to the vat.")
|
||||
|
||||
return@queueScript delayScript(player, Animation(2295).duration + 1)
|
||||
}
|
||||
1 -> {
|
||||
if (!removeItem(player, Item(Items.BARLEY_MALT_6008, 1))) { return@queueScript stopExecuting(player) }
|
||||
|
||||
stopWalk(player)
|
||||
animate(player, Animation(2295))
|
||||
|
||||
val vatVarbit = varbits.getValue(player.location.regionId).varbit
|
||||
setVarbit(player, vatVarbit, 2, true)
|
||||
|
||||
return@queueScript delayScript(player, Animation(2295).duration + 1)
|
||||
}
|
||||
2 -> {
|
||||
stopWalk(player)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
else -> return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
sendDialogue(player, "You need 2 barley malt.")
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Add The Stuff to a vat with malt
|
||||
onUseWith(SCENERY, Items.THE_STUFF_8988, Scenery.FERMENTING_VAT_7441) { player, _, _ ->
|
||||
if (inInventory(player, Items.THE_STUFF_8988, 1)) {
|
||||
getVat(player).addTheStuff()
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Add ingredients to a vat with malt
|
||||
onUseWith(SCENERY, aleIngredientsId, Scenery.FERMENTING_VAT_7441) { player, used, _ ->
|
||||
if (used.id == Items.APPLE_MUSH_5992) {
|
||||
sendDialogue(player, "Apple mush needs to be added to an empty vat.")
|
||||
return@onUseWith false
|
||||
}
|
||||
// If you try to make Kelda Stout in Port Phasmatys the vat deletes itself forever
|
||||
if (used.id == Items.KELDA_HOPS_6113 && player.location.regionId != KELDAGRIM_LOC) {
|
||||
sendMessage(player, "Nothing interesting happens.")
|
||||
return@onUseWith false
|
||||
}
|
||||
// Branch off into Kelda Stout logic
|
||||
if (used.id == Items.KELDA_HOPS_6113 && player.location.regionId == KELDAGRIM_LOC) {
|
||||
KeldaBypasses.keldaAddHops(player)
|
||||
return@onUseWith true
|
||||
}
|
||||
getVat(player).addIngredient(used as Item)
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Add yeast to a vat that's ready to brew
|
||||
onUseWith(SCENERY, Items.ALE_YEAST_5767, *vatsNeedingYeast) { player, _, with ->
|
||||
if (inInventory(player, Items.ALE_YEAST_5767, 1)) {
|
||||
// Branch off into Kelda Stout logic
|
||||
if (with.id == Scenery.FERMENTING_VAT_8871 &&
|
||||
player.location.regionId == KELDAGRIM_LOC &&
|
||||
getVarbit(player, 736) == 68) { // 68 = Kelda MIXED state
|
||||
KeldaBypasses.keldaAddYeast(player)
|
||||
return@onUseWith true
|
||||
}
|
||||
getVat(player).addYeast()
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Turn the valve to empty a vat into a barrel
|
||||
on(intArrayOf(Scenery.VALVE_7442, Scenery.VALVE_7443), SCENERY, "turn") { player, _ ->
|
||||
// Branch off into Kelda Stout logic
|
||||
when {
|
||||
(player.location.regionId == KELDAGRIM_LOC && KeldaBypasses.keldaIsBrewing(player)) ->
|
||||
sendMessage(player, "The contents of the vat haven't finished fermenting yet.")
|
||||
(player.location.regionId == KELDAGRIM_LOC && KeldaBypasses.keldaIsComplete(player)) ->
|
||||
KeldaBypasses.keldaTurnValve(player)
|
||||
else -> getVat(player).turnValve()
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// Drain a barrel of bad ale
|
||||
on(drainBarrels, SCENERY, "Drain") { player, _ ->
|
||||
getVat(player).drainBarrel()
|
||||
return@on true
|
||||
}
|
||||
|
||||
// Level a barrel (clicking on it)
|
||||
on(barrels, SCENERY, "Level") { player, _ ->
|
||||
queueScript(player, 1) {
|
||||
val goAgain : Boolean
|
||||
if (inInventory(player, Items.BEER_GLASS_1919)) {
|
||||
goAgain = getVat(player).levelBarrel(Items.BEER_GLASS_1919)
|
||||
}
|
||||
else if (inInventory(player, Items.CALQUAT_KEG_5769)) {
|
||||
goAgain = getVat(player).levelBarrel(Items.CALQUAT_KEG_5769)
|
||||
}
|
||||
else{
|
||||
goAgain = false
|
||||
sendDialogue(player, "You need a container to level the barrel.")
|
||||
}
|
||||
return@queueScript if(goAgain) delayScript(player, 1) else stopExecuting(player)
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// Level a barrel (using a container on it)
|
||||
onUseWith(SCENERY, intArrayOf(Items.BEER_GLASS_1919, Items.CALQUAT_KEG_5769), *barrels) { player, used, _ ->
|
||||
getVat(player).levelBarrel(used.id)
|
||||
return@onUseWith true
|
||||
}
|
||||
}
|
||||
|
||||
private fun getVat(player: Player) : Vat {
|
||||
return varbits.getValue(player.location.regionId).getVat(player)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
package content.global.skill.cooking.fermenting
|
||||
|
||||
import core.api.*
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import org.rs09.consts.Items
|
||||
|
||||
object CalquatDecant {
|
||||
|
||||
// Consumables.java hurts my eyes so I did this instead
|
||||
data class CalquatKegs (
|
||||
val emptyKeg: Int = Items.CALQUAT_KEG_5769,
|
||||
val fourDose: Int, val threeDose: Int = fourDose - 2, val twoDose: Int = threeDose - 2, val oneDose: Int = twoDose - 2, val glass: Int,
|
||||
val fourMature: Int, val threeMature: Int = fourMature - 2, val twoMature: Int = threeMature - 2, val oneMature: Int = twoMature - 2, val matureGlass: Int
|
||||
)
|
||||
|
||||
val calquatDrinks =
|
||||
Brewable.values()
|
||||
.filter { it.product.size == 4 }
|
||||
.associateWith { CalquatKegs (
|
||||
fourDose = it.product[1],
|
||||
glass = it.product[0],
|
||||
fourMature = it.product[3],
|
||||
matureGlass = it.product[2]
|
||||
)
|
||||
}
|
||||
|
||||
// Array of all kegs with nonzero contents
|
||||
val allKegIds: IntArray by lazy {
|
||||
calquatDrinks.values.flatMap { keg ->
|
||||
listOf(
|
||||
keg.fourDose, keg.threeDose, keg.twoDose, keg.oneDose,
|
||||
keg.fourMature, keg.threeMature, keg.twoMature, keg.oneMature
|
||||
)
|
||||
}.distinct().toIntArray()
|
||||
}
|
||||
|
||||
// Reverse lookup map
|
||||
private val kegLookupMap: Map<Int, Pair<Brewable, CalquatKegs>> by lazy {
|
||||
calquatDrinks.entries.flatMap { (brewable, kegs) ->
|
||||
listOf(
|
||||
kegs.fourDose, kegs.threeDose, kegs.twoDose, kegs.oneDose,
|
||||
kegs.fourMature, kegs.threeMature, kegs.twoMature, kegs.oneMature
|
||||
).map { itemId -> itemId to (brewable to kegs) }
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
// Check what a keg is so it can be related to other kegs
|
||||
fun getKeg(itemId: Int): Pair<Brewable, CalquatKegs>? = kegLookupMap[itemId]
|
||||
|
||||
// Check if an item is a keg
|
||||
fun isKeg(itemId: Int): Boolean = kegLookupMap.containsKey(itemId)
|
||||
|
||||
// Check how full a keg is
|
||||
fun getDoseByKeg(itemId: Int, kegs: CalquatKegs): Int {
|
||||
return when (itemId) {
|
||||
kegs.fourDose, kegs.fourMature -> 4
|
||||
kegs.threeDose, kegs.threeMature -> 3
|
||||
kegs.twoDose, kegs.twoMature -> 2
|
||||
kegs.oneDose, kegs.oneMature -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a keg contains matured ale
|
||||
fun isMature(itemId: Int, kegs: CalquatKegs): Boolean {
|
||||
return itemId == kegs.fourMature || itemId == kegs.threeMature ||
|
||||
itemId == kegs.twoMature || itemId == kegs.oneMature
|
||||
}
|
||||
|
||||
// Identifies a keg's category by its contents
|
||||
fun getKegByDose(kegs: CalquatKegs, dose: Int, mature: Boolean): Int {
|
||||
return if (!mature) {
|
||||
when (dose) {
|
||||
4 -> kegs.fourDose
|
||||
3 -> kegs.threeDose
|
||||
2 -> kegs.twoDose
|
||||
1 -> kegs.oneDose
|
||||
else -> kegs.emptyKeg
|
||||
}
|
||||
} else {
|
||||
when (dose) {
|
||||
4 -> kegs.fourMature
|
||||
3 -> kegs.threeMature
|
||||
2 -> kegs.twoMature
|
||||
1 -> kegs.oneMature
|
||||
else -> kegs.emptyKeg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decanting function (NPC) - should work the same as potions
|
||||
fun decantCalquat(container: core.game.container.Container): Pair<List<Item>, List<Item>> {
|
||||
val doseCount = HashMap<Pair<Brewable, Boolean>, Int>() // (Brewable, isMature), Total Doses
|
||||
val toRemove = ArrayList<Item>()
|
||||
val toAdd = ArrayList<Item>()
|
||||
|
||||
// Count up all doses grouped by keg type and maturity
|
||||
for (item in container.toArray()) {
|
||||
if (item == null) continue
|
||||
val kegInfo = getKeg(item.id) ?: continue
|
||||
val (brewable, kegs) = kegInfo
|
||||
val dose = getDoseByKeg(item.id, kegs)
|
||||
if (dose == 0) continue
|
||||
val mature = isMature(item.id, kegs)
|
||||
val key = brewable to mature
|
||||
doseCount[key] = (doseCount[key] ?: 0) + dose
|
||||
toRemove.add(item)
|
||||
}
|
||||
|
||||
// Convert doses back into kegs
|
||||
for ((key, totalDoses) in doseCount) {
|
||||
val (brewable, mature) = key
|
||||
val kegs = calquatDrinks[brewable] ?: continue
|
||||
|
||||
val fullKegs = totalDoses / 4 // Number of 4-dose kegs
|
||||
val remainderDose = totalDoses % 4 // Leftover doses
|
||||
|
||||
if (fullKegs > 0)
|
||||
toAdd.add(Item(getKegByDose(kegs, 4, mature), fullKegs))
|
||||
if (remainderDose > 0)
|
||||
toAdd.add(Item(getKegByDose(kegs, remainderDose, mature)))
|
||||
}
|
||||
|
||||
// Add empty kegs for removed containers
|
||||
val emptyKegs = toRemove.size - toAdd.sumOf { it.amount }
|
||||
if (emptyKegs > 0) {
|
||||
val firstKegs = calquatDrinks.values.firstOrNull()
|
||||
if (firstKegs != null)
|
||||
toAdd.add(Item(firstKegs.emptyKeg, emptyKegs))
|
||||
}
|
||||
|
||||
return Pair(toRemove, toAdd)
|
||||
}
|
||||
|
||||
// Decanting function (manual) - should work the same as potions
|
||||
fun combineKegs(player: Player, used: Item, with: Item): Boolean {
|
||||
// Identify both kegs
|
||||
val (usedBrewable, kegs) = getKeg(used.id) ?: return false
|
||||
val (withBrewable, _) = getKeg(with.id) ?: return false
|
||||
|
||||
// Verify the same drink is in both kegs
|
||||
if (usedBrewable != withBrewable) return false
|
||||
|
||||
val usedMature = isMature(used.id, kegs)
|
||||
val withMature = isMature(with.id, kegs)
|
||||
|
||||
// Verify the same maturity status is in both kegs
|
||||
if (usedMature != withMature) return false
|
||||
|
||||
// Get doses
|
||||
val usedDose = getDoseByKeg(used.id, kegs)
|
||||
val withDose = getDoseByKeg(with.id, kegs)
|
||||
|
||||
// Disallow combining full or empty kegs
|
||||
if (usedDose == 4 || withDose == 4 || usedDose == 0 || withDose == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
val totalDosage = usedDose + withDose
|
||||
val fullDoses = totalDosage / 4
|
||||
val leftoverDose = totalDosage % 4
|
||||
|
||||
// Replace the targeted keg with a (4) dose keg
|
||||
if (fullDoses != 0) {
|
||||
replaceSlot(player, with.slot, Item(getKegByDose(kegs, 4, usedMature)), with, Container.INVENTORY)
|
||||
}
|
||||
|
||||
// Replace the targeted keg with the updated dosage amount
|
||||
if (leftoverDose != 0 && fullDoses == 0) {
|
||||
replaceSlot(player, with.slot, Item(getKegByDose(kegs, leftoverDose, usedMature)), with, Container.INVENTORY)
|
||||
|
||||
// Replace the used with keg with the updated dosage amount
|
||||
} else if (leftoverDose != 0) {
|
||||
replaceSlot(player, used.slot, Item(getKegByDose(kegs, leftoverDose, usedMature)), used, Container.INVENTORY)
|
||||
}
|
||||
|
||||
// Replace the used with keg with an empty keg
|
||||
if (leftoverDose == 0 || fullDoses == 0) {
|
||||
replaceSlot(player, used.slot, Item(kegs.emptyKeg), used, Container.INVENTORY)
|
||||
}
|
||||
|
||||
// Send message/Play sound
|
||||
val amountString = when {
|
||||
totalDosage >= 4 -> "four"
|
||||
totalDosage == 3 -> "three"
|
||||
else -> "two"
|
||||
}
|
||||
|
||||
sendMessage(player, "You have combined the kegs into $amountString pints.")
|
||||
playAudio(player, 2401)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Decant keg into beer glass
|
||||
fun pourIntoGlass(player: Player, glass: Item, keg: Item): Boolean {
|
||||
// Find keg info
|
||||
val kegInfo = getKeg(keg.id) ?: return false
|
||||
val (_, kegs) = kegInfo
|
||||
val currentDose = getDoseByKeg(keg.id, kegs)
|
||||
val mature = isMature(keg.id, kegs)
|
||||
if (currentDose == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Fill the glass
|
||||
val glassItem = if (mature) kegs.matureGlass else kegs.glass
|
||||
replaceSlot(player, glass.slot, Item(glassItem), glass, Container.INVENTORY)
|
||||
|
||||
// Drain the keg
|
||||
val newDose = currentDose - 1
|
||||
replaceSlot(player, keg.slot, Item(getKegByDose(kegs, newDose, mature)), keg, Container.INVENTORY)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package content.global.skill.cooking.fermenting
|
||||
|
||||
import core.api.getVarbit
|
||||
import core.api.sendMessage
|
||||
import core.game.node.entity.Entity
|
||||
import core.api.*
|
||||
import core.game.interaction.QueueStrength
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import org.rs09.consts.Items
|
||||
import core.game.system.timer.PersistTimer
|
||||
import core.game.system.timer.RSTimer
|
||||
|
||||
/**
|
||||
* Independent brewing timer and functions exclusively for Kelda Stout
|
||||
* Varbit progression: 68 (mixed) -> 69 (brewing1) -> 70 (brewing2) -> 71 (done)
|
||||
*/
|
||||
|
||||
class KeldaStoutGrowth : PersistTimer( 5 /*minutes*/ * 100 /*ticks per minute*/, "cooking:brewing-kelda", isSoft = true) {
|
||||
lateinit var player: Player
|
||||
|
||||
override fun run(entity: Entity): Boolean {
|
||||
player = entity as Player
|
||||
when (val currentVarbit = getVarbit(player, 736)) {
|
||||
69 -> {
|
||||
setVarbit(player, 736, currentVarbit + 1)
|
||||
return true
|
||||
}
|
||||
70 -> {
|
||||
setVarbit(player, 736, currentVarbit + 1)
|
||||
sendMessage(player, "Perhaps I should have a look and see if my Kelda Stout has brewed...")
|
||||
return false
|
||||
}
|
||||
else -> {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTimer(vararg args: Any): RSTimer {
|
||||
return KeldaStoutGrowth()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object KeldaBypasses {
|
||||
|
||||
fun keldaAddHops(player: Player) {
|
||||
queueScript(player, 0) {
|
||||
stopWalk(player)
|
||||
if (!removeItem(player, Item(Items.KELDA_HOPS_6113, 1))) { return@queueScript stopExecuting(player) }
|
||||
sendMessage(player, "You add some Kelda hops to the vat.")
|
||||
lock(player, Animation(2295).duration + 1)
|
||||
animate(player, Animation(2295))
|
||||
setVarbit(player, 736, getVarbit(player, 736) + 66)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
|
||||
fun keldaAddYeast(player: Player) {
|
||||
//addYeast() override
|
||||
queueScript(player, 0) {
|
||||
|
||||
if (!removeItem(player, Item(Items.ALE_YEAST_5767, 1))) { return@queueScript stopExecuting(player) }
|
||||
addItem(player, Items.EMPTY_POT_1931)
|
||||
|
||||
lock(player, Animation(2283).duration)
|
||||
animate(player, Animation(2295))
|
||||
|
||||
sendMessage(player, "You add a pot of ale yeast to the vat.")
|
||||
sendMessage(player, "The contents of the vat have begun to ferment.")
|
||||
|
||||
setVarbit(player, 736, getVarbit(player, 736) + 1)
|
||||
registerTimer(player, KeldaStoutGrowth())
|
||||
return@queueScript true
|
||||
}
|
||||
}
|
||||
|
||||
fun keldaTurnValve(player: Player) {
|
||||
queueScript(player, 0, QueueStrength.SOFT) {
|
||||
|
||||
lock(player, Animation(780).duration)
|
||||
animate(player, Animation(780))
|
||||
|
||||
sendMessage(player, "You turn the valve.")
|
||||
|
||||
delayScript(player, 2)
|
||||
|
||||
// Fill the barrel, then empty the vat
|
||||
setVarbit(player, 738, 3, true)
|
||||
setVarbit(player, 736, 0, true)
|
||||
|
||||
sendMessage(player, "The barrel now contains 1 pint of Kelda Stout.")
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
|
||||
fun keldaIsBrewing(player: Player): Boolean {
|
||||
val vatVarbit = getVarbit(player, 736)
|
||||
return vatVarbit in 69..70
|
||||
}
|
||||
|
||||
fun keldaIsComplete(player: Player): Boolean {
|
||||
val vatVarbit = getVarbit(player, 736)
|
||||
return vatVarbit == 71
|
||||
}
|
||||
}
|
||||
366
Server/src/main/content/global/skill/cooking/fermenting/Vat.kt
Normal file
366
Server/src/main/content/global/skill/cooking/fermenting/Vat.kt
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
package content.global.skill.cooking.fermenting
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.QueueStrength
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.Items
|
||||
|
||||
|
||||
class Vat (var player: Player, private val brewingVat : BrewingVat, var theStuff: Boolean,
|
||||
var nextBrew: Long, var stage: BrewingStage,
|
||||
var brewingItem : Brewable?) {
|
||||
|
||||
private var barrelItem: Brewable? = null
|
||||
|
||||
constructor(player: Player, brewingVat: BrewingVat) :
|
||||
this(player, brewingVat, false, 0, BrewingStage.EMPTY, null)
|
||||
|
||||
fun addTheStuff() {
|
||||
if (!theStuff && removeItem(player, Item(Items.THE_STUFF_8988, 1))) {
|
||||
theStuff = true
|
||||
|
||||
lock(player, Animation(2283).duration)
|
||||
animate(player, Animation(2283))
|
||||
|
||||
sendMessage(player, "You add The Stuff to the mixture in the vat.")
|
||||
|
||||
} else { sendMessage(player, "Nothing interesting happens.") }
|
||||
}
|
||||
|
||||
fun addIngredient(item: Item): Boolean {
|
||||
val ingredient = Brewable.getBrewable(item.id) ?: return false
|
||||
if (getDynLevel(player, Skills.COOKING) < ingredient.level) {
|
||||
sendDialogue(player, "You need a Cooking level of at least ${ingredient.level} in order to brew ${ingredient.displayName}.")
|
||||
return false
|
||||
}
|
||||
if (!inInventory(player, item.id, ingredient.ingredientAmount)) {
|
||||
val ingredientWord = if (ingredient.itemID == Items.MUSHROOM_6004) item.name+"s" else item.name
|
||||
sendDialogue(player, "You need 4 $ingredientWord.")
|
||||
return false
|
||||
}
|
||||
queueScript(player, 0, QueueStrength.SOFT) { counter ->
|
||||
when (counter) {
|
||||
0 -> {
|
||||
if (!removeItem(player, Item(item.id, 1))) { return@queueScript stopExecuting(player) }
|
||||
if (ingredient.itemID == Items.APPLE_MUSH_5992) {
|
||||
addItem(player, Items.BUCKET_1925)
|
||||
}
|
||||
|
||||
stopWalk(player)
|
||||
animate(player, Animation(2295))
|
||||
|
||||
val ingredientWord = if (item.id == Items.MUSHROOM_6004) item.name+"s" else item.name
|
||||
sendMessage(player, "You add some $ingredientWord to the vat.")
|
||||
|
||||
if (ingredient.ingredientAmount == 1) {
|
||||
lock(player, Animation(2295).duration)
|
||||
|
||||
brewingItem = ingredient
|
||||
stage = BrewingStage.MIXED
|
||||
updateVat()
|
||||
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
|
||||
lock(player, Animation(2295).duration * 4 + 3)
|
||||
return@queueScript delayScript(player, Animation(2295).duration + 1)
|
||||
}
|
||||
1, 2 -> {
|
||||
if (!removeItem(player, Item(item.id, 1))) { return@queueScript stopExecuting(player) }
|
||||
if (ingredient.itemID == Items.APPLE_MUSH_5992) {
|
||||
addItem(player, Items.BUCKET_1925)
|
||||
}
|
||||
|
||||
stopWalk(player)
|
||||
animate(player, Animation(2295))
|
||||
|
||||
return@queueScript delayScript(player, Animation(2295).duration + 1)
|
||||
}
|
||||
3 -> {
|
||||
if (!removeItem(player, Item(item.id, 1))) { return@queueScript stopExecuting(player) }
|
||||
if (ingredient.itemID == Items.APPLE_MUSH_5992) {
|
||||
addItem(player, Items.BUCKET_1925)
|
||||
}
|
||||
|
||||
stopWalk(player)
|
||||
animate(player, Animation(2295))
|
||||
|
||||
brewingItem = ingredient
|
||||
stage = BrewingStage.MIXED
|
||||
updateVat()
|
||||
|
||||
return@queueScript delayScript(player, Animation(2295).duration)
|
||||
}
|
||||
4 -> {
|
||||
stopWalk(player)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
else -> return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun addYeast() {
|
||||
if (removeItem(player, Item(Items.ALE_YEAST_5767, 1))) {
|
||||
addItem(player, Items.EMPTY_POT_1931)
|
||||
|
||||
lock(player, Animation(2283).duration)
|
||||
animate(player, Animation(2295))
|
||||
|
||||
sendMessage(player, "You add a pot of ale yeast to the vat.")
|
||||
sendMessage(player, "The contents of the vat have begun to ferment.")
|
||||
|
||||
stage = BrewingStage.BREWING1
|
||||
nextBrew = System.currentTimeMillis() + (brewCycleTime * 600) // ticks to ms
|
||||
updateVat()
|
||||
} else {
|
||||
sendMessage(player, "Nothing interesting happens.")
|
||||
}
|
||||
}
|
||||
|
||||
fun brew(forceStep: Boolean = false, forceGood: Boolean = false, forceMature: Boolean = false, forceBad: Boolean = false) : Boolean {
|
||||
val resp = internalBrew(forceStep, forceGood, forceMature, forceBad)
|
||||
updateVat()
|
||||
return resp
|
||||
}
|
||||
|
||||
fun canBrew() : Boolean {
|
||||
return listOf(BrewingStage.BREWING1, BrewingStage.BREWING2).any { it == stage }
|
||||
}
|
||||
|
||||
/** The meat of the brewing mechanic lives in the following internalBrew function
|
||||
* No one has clearly documented how this should work
|
||||
* Period sources are vague, unaccountable, and/or vary wildly in their claims
|
||||
* https://x.com/JagexAsh/status/994867369460813824
|
||||
* Ash states that it was challenging to follow when he was trying to do it in OSRS
|
||||
*
|
||||
* If a good source is uncovered, this section can easily be configured to match it
|
||||
*
|
||||
* Current config results in the following behavior:
|
||||
* Cooking level does not play a factor
|
||||
* Chance to advance a step (or go bad) - 20%
|
||||
* Chance to go bad when advancing - 25%
|
||||
* Chance to mature (w/o The Stuff) - 05%
|
||||
* Chance to mature (w/ The Stuff) - 50%
|
||||
* Time per cycle (in BrewGrowth.kt) - 6 hours, or 36000 ticks
|
||||
*/
|
||||
|
||||
private fun internalBrew(forceStep: Boolean, forceGood: Boolean, forceMature: Boolean, forceBad: Boolean):Boolean {
|
||||
nextBrew = System.currentTimeMillis() + (brewCycleTime * 600) // Ticks to ms
|
||||
if (listOf(BrewingStage.BREWING1, BrewingStage.BREWING2).all{ it != stage}) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Step advancement check
|
||||
if (RandomFunction.random(5) == 0 || forceStep || forceGood || forceMature || forceBad) {
|
||||
if ((RandomFunction.random(4) == 0 || forceBad) && !forceGood && !forceMature) {
|
||||
stage = BrewingStage.BAD
|
||||
return false
|
||||
}
|
||||
stage = if (stage == BrewingStage.BREWING1) BrewingStage.BREWING2 else BrewingStage.DONE
|
||||
}
|
||||
|
||||
// Maturity check
|
||||
if (stage == BrewingStage.DONE) {
|
||||
sendMessage(player, "Perhaps I should have a look and see if my ${brewingItem!!.displayName} has brewed...")
|
||||
val roll = if (theStuff) 2 else 20
|
||||
if ((RandomFunction.random(roll) == 0 || forceMature) && !forceGood) {
|
||||
stage = BrewingStage.MATURE
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun turnValve(){
|
||||
if (stage == BrewingStage.EMPTY) {
|
||||
queueScript(player, 1) {
|
||||
sendMessage(player, "Nothing interesting happens.")
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (getVarbit(player,this.brewingVat.varbit + 2) > 0) {
|
||||
queueScript(player, 1) {
|
||||
sendMessage(player, "The barrel is already full.")
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (stage == BrewingStage.BREWING1 || stage == BrewingStage.BREWING2) {
|
||||
queueScript(player,1) {
|
||||
sendMessage(player, "The contents of the vat haven't finished fermenting yet.")
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
return
|
||||
}
|
||||
lock(player, Animation(780).duration)
|
||||
animate(player, Animation(780))
|
||||
|
||||
sendMessage(player, "You turn the valve.")
|
||||
|
||||
if (stage == BrewingStage.MATURE || stage == BrewingStage.DONE) {
|
||||
rewardXP(player, Skills.COOKING, brewingItem!!.levelXP)
|
||||
queueScript(player, 2, QueueStrength.SOFT) {
|
||||
sendMessage(player, "The barrel now contains 8 pints of ${brewingItem!!.displayName}.")
|
||||
fillBarrel()
|
||||
emptyVat()
|
||||
updateVat()
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
} else {
|
||||
fillBarrel()
|
||||
emptyVat()
|
||||
updateVat()
|
||||
}
|
||||
}
|
||||
|
||||
private fun emptyVat() {
|
||||
brewingItem = null
|
||||
theStuff = false
|
||||
stage = BrewingStage.EMPTY
|
||||
updateVat()
|
||||
}
|
||||
|
||||
private fun fillBarrel() {
|
||||
var varbitValue = 0
|
||||
when (stage){
|
||||
BrewingStage.EMPTY -> Unit
|
||||
BrewingStage.WATER,
|
||||
BrewingStage.MALT,
|
||||
BrewingStage.MIXED,
|
||||
BrewingStage.BREWING1,
|
||||
BrewingStage.BREWING2 -> {
|
||||
varbitValue = 4
|
||||
}
|
||||
BrewingStage.DONE,
|
||||
BrewingStage.MATURE -> {
|
||||
barrelItem = brewingItem
|
||||
val barrelAmount = 8
|
||||
|
||||
varbitValue = barrelItem?.barrelVarBitOffset ?: 0
|
||||
|
||||
if (barrelItem != null){
|
||||
varbitValue += (barrelAmount - 1)
|
||||
if (stage == BrewingStage.MATURE) {
|
||||
varbitValue += 128
|
||||
}
|
||||
}
|
||||
}
|
||||
BrewingStage.BAD ->{
|
||||
varbitValue = if(brewingItem == Brewable.CIDER) 2 else 1
|
||||
}
|
||||
}
|
||||
setVarbit(player, this.brewingVat.varbit + 2, varbitValue, true)
|
||||
}
|
||||
|
||||
fun levelBarrel(container: Int): Boolean {
|
||||
var value = getVarbit(player, this.brewingVat.varbit + 2)
|
||||
if (value == 0) return false
|
||||
|
||||
// Check for mature bit (128) and strip it for calculations
|
||||
val isMature = value >= 128
|
||||
val baseValue = if (isMature) value - 128 else value
|
||||
var servingsLeft = if(baseValue == 3) 1 else (baseValue % 8) + 1
|
||||
|
||||
val brewable =
|
||||
if (baseValue == 3) Brewable.KELDA_STOUT
|
||||
else Brewable.values()[(baseValue/8) - 1]
|
||||
|
||||
val product = brewable.product
|
||||
|
||||
if (container == Items.BEER_GLASS_1919) {
|
||||
if(removeItem(player, container)) {
|
||||
animate(player, Animation(2285))
|
||||
value--
|
||||
servingsLeft--
|
||||
val productIndex = if (isMature) 2 else 0
|
||||
sendMessage(player, "You pour a glass of ${brewable.displayName}.")
|
||||
addItem(player, product[productIndex])
|
||||
}
|
||||
}
|
||||
else if (brewable == Brewable.KELDA_STOUT) {
|
||||
sendDialogue(player, "You must use a glass to level Kelda Stout.")
|
||||
return false
|
||||
}
|
||||
else if (container == Items.CALQUAT_KEG_5769) {
|
||||
if(removeItem(player, container)){
|
||||
animate(player, Animation(2284))
|
||||
val servingsToTake = minOf(servingsLeft, 4)
|
||||
value -= servingsToTake
|
||||
servingsLeft -= servingsToTake
|
||||
val filledKeg = CalquatDecant.calquatDrinks[brewable]!!
|
||||
addItem(player, CalquatDecant.getKegByDose(filledKeg, servingsToTake, isMature))
|
||||
}
|
||||
}
|
||||
if (servingsLeft == 0) {
|
||||
sendMessage(player, "The barrel is now empty.")
|
||||
value = 0
|
||||
}
|
||||
setVarbit(player, this.brewingVat.varbit + 2, value, true)
|
||||
return servingsLeft != 0
|
||||
}
|
||||
|
||||
fun drainBarrel() {
|
||||
sendMessage(player, "You drain the barrel.")
|
||||
setVarbit(player, this.brewingVat.varbit + 2, 0, true)
|
||||
|
||||
}
|
||||
|
||||
fun isVatEmpty(): Boolean {
|
||||
return stage == BrewingStage.EMPTY
|
||||
}
|
||||
|
||||
fun isBarrelEmpty(): Boolean {
|
||||
return getVarbit(player, (this.brewingVat.varbit + 2)) == 0
|
||||
}
|
||||
|
||||
private fun getVatDisplay(): Int {
|
||||
return when (stage){
|
||||
BrewingStage.EMPTY -> 0
|
||||
BrewingStage.WATER -> 1
|
||||
BrewingStage.MALT -> 2
|
||||
BrewingStage.MIXED -> brewingItem?.vatVarBitOffset ?: run { 0 }
|
||||
BrewingStage.BREWING1 -> (brewingItem?.vatVarBitOffset?.plus(1)) ?: run { 0 }
|
||||
BrewingStage.BREWING2 -> (brewingItem?.vatVarBitOffset?.plus(2)) ?: run { 0 }
|
||||
BrewingStage.DONE -> (brewingItem?.vatVarBitOffset?.plus(3)) ?: run { 0 }
|
||||
BrewingStage.MATURE -> (brewingItem?.vatVarBitOffset?.plus(4)) ?: run { 0 }
|
||||
BrewingStage.BAD -> if (brewingItem == Brewable.CIDER) 65 else 64
|
||||
}
|
||||
}
|
||||
|
||||
fun updateVat(){
|
||||
setVarbit(player, this.brewingVat.varbit, this.getVatDisplay(), true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
enum class BrewingStage {
|
||||
EMPTY,
|
||||
WATER,
|
||||
MALT,
|
||||
MIXED,
|
||||
BREWING1,
|
||||
BREWING2,
|
||||
DONE,
|
||||
MATURE,
|
||||
BAD;
|
||||
}
|
||||
|
||||
|
||||
enum class BrewingVat(val varbit: Int) {
|
||||
KELDAGRIM(736),
|
||||
PORT_PHAS(737);
|
||||
|
||||
fun getVat(player: Player): Vat {
|
||||
val vat = getOrStartTimer<BrewGrowth>(player)
|
||||
return vat.getVat(this, true)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package content.global.skill.farming
|
||||
|
||||
import content.data.Quests
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialogueFile
|
||||
import core.game.dialogue.FacialExpression
|
||||
|
|
@ -67,6 +68,8 @@ class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false):
|
|||
val amount = if (item?.amount == 1) "one" else item?.amount
|
||||
npc(FacialExpression.HAPPY, "I want $amount $protectionText, one monkey bar,", "and one ground tooth for that.")
|
||||
stage = 200
|
||||
} else if (patch.patch.type == PatchType.CALQUAT_TREE_PATCH && getQuestStage(player!!, Quests.JUNGLE_POTION) < 100) {
|
||||
npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE } // Cannot confirm authenticity of this line just that there should be a rejection here
|
||||
} else if (quickPay && !(inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount))) {
|
||||
val amount = if (item?.amount == 1) "one" else item?.amount
|
||||
npc(FacialExpression.HAPPY, "I want $amount $protectionText for that.")
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ enum class Farmers(val id: Int, val patches: Array<FarmingPatch>) {
|
|||
TORRELL(2338, arrayOf(FarmingPatch.ARDOUGNE_BUSH)),
|
||||
FRIZZY(4560, arrayOf(FarmingPatch.PORT_SARIM_SPIRIT_TREE)),
|
||||
YULF(4561, arrayOf(FarmingPatch.ETCETERIA_SPIRIT_TREE)),
|
||||
PRAISTAN(4562, arrayOf(FarmingPatch.KARAMJA_SPIRIT_TREE))
|
||||
PRAISTAN(4562, arrayOf(FarmingPatch.KARAMJA_SPIRIT_TREE)),
|
||||
IMIAGO(8041, arrayOf(FarmingPatch.CALQUAT_TREE))
|
||||
;
|
||||
|
||||
companion object{
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ enum class FarmingPatch(val varbit: Int, val type: PatchType) {
|
|||
DRAYNOR_BELLADONNA(748, PatchType.BELLADONNA_PATCH),
|
||||
CANIFIS_MUSHROOM(746, PatchType.MUSHROOM_PATCH),
|
||||
ALKHARID_CACTUS(744, PatchType.CACTUS_PATCH),
|
||||
EVIL_TURNIP(4291, PatchType.EVIL_TURNIP_PATCH);
|
||||
EVIL_TURNIP(4291, PatchType.EVIL_TURNIP_PATCH),
|
||||
CALQUAT_TREE(726, PatchType.CALQUAT_TREE_PATCH);
|
||||
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@ class HealthChecker : OptionHandler() {
|
|||
patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 3)
|
||||
sendMessage(player, "You examine the cactus for signs of disease and find that it is in perfect health.")
|
||||
}
|
||||
PatchType.CALQUAT_TREE_PATCH -> {
|
||||
patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 6)
|
||||
sendMessage(player, "You examine the calquat tree for signs of disease and find that it is in perfect health.")
|
||||
}
|
||||
else -> log(this::class.java, Log.ERR, "Unreachable patch type from when(type) switch in HealthChecker.kt")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||
var compost = CompostType.NONE
|
||||
var protectionPaid = false
|
||||
var cropLives = 3
|
||||
val checkablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CACTUS_PATCH)
|
||||
val checkablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CACTUS_PATCH, PatchType.CALQUAT_TREE_PATCH)
|
||||
|
||||
fun setNewHarvestAmount() {
|
||||
val compostMod = when(compost) {
|
||||
|
|
@ -164,6 +164,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||
PatchType.CACTUS_PATCH -> setVarbit(player, patch.varbit, 31)
|
||||
PatchType.TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages)
|
||||
PatchType.SPIRIT_TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 24)
|
||||
PatchType.CALQUAT_TREE_PATCH -> setVarbit(player, patch.varbit, 34)
|
||||
else -> log(this::class.java, Log.WARN, "Invalid setting of isCheckHealth for patch type: " + patch.type.name + "at" + patch.name)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -223,7 +224,13 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||
if(isDead) setVisualState(getMushroomDeathValue())
|
||||
else if(isDiseased && !isDead) setVisualState(getMushroomDiseaseValue())
|
||||
}
|
||||
else -> {}
|
||||
PatchType.CALQUAT_TREE_PATCH -> {
|
||||
if(isDead) setVisualState(getCalquatDeathValue())
|
||||
else if(isDiseased && !isDead) setVisualState(getCalquatDiseaseValue())
|
||||
}
|
||||
else -> {
|
||||
log(this::class.java, Log.WARN, "Invalid patch type attempted to be displayed: " + patch.type.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -310,6 +317,14 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||
return (plantable?.value ?: 0) + currentGrowthStage + 16
|
||||
}
|
||||
|
||||
private fun getCalquatDiseaseValue(): Int {
|
||||
return (plantable?.value ?: 0) + currentGrowthStage + 14
|
||||
}
|
||||
|
||||
private fun getCalquatDeathValue(): Int {
|
||||
return (plantable?.value ?: 0) + currentGrowthStage + 21
|
||||
}
|
||||
|
||||
private fun getHerbDiseaseValue(): Int {
|
||||
return if (plantable?.value ?: -1 <= 103) {
|
||||
128 + (((plantable?.ordinal ?: 0) - Plantable.GUAM_SEED.ordinal) * 3) + currentGrowthStage - 1
|
||||
|
|
@ -364,8 +379,12 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||
isCheckHealth = true
|
||||
}
|
||||
|
||||
if((patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.BUSH_PATCH || patch.type == PatchType.CACTUS_PATCH) && plantable?.stages == currentGrowthStage){
|
||||
if((patch.type == PatchType.BUSH_PATCH && getFruitOrBerryCount() < 4) || (patch.type == PatchType.FRUIT_TREE_PATCH && getFruitOrBerryCount() < 6) || (patch.type == PatchType.CACTUS_PATCH && getFruitOrBerryCount() < 3)){
|
||||
if(arrayOf(PatchType.FRUIT_TREE_PATCH, PatchType.BUSH_PATCH, PatchType.CACTUS_PATCH, PatchType.CALQUAT_TREE_PATCH).any{ patch.type == it} && plantable?.stages == currentGrowthStage){
|
||||
if((patch.type == PatchType.BUSH_PATCH && getFruitOrBerryCount() < 4) ||
|
||||
(patch.type == PatchType.FRUIT_TREE_PATCH && getFruitOrBerryCount() < 6) ||
|
||||
(patch.type == PatchType.CACTUS_PATCH && getFruitOrBerryCount() < 3) ||
|
||||
(patch.type == PatchType.CALQUAT_TREE_PATCH && getFruitOrBerryCount() < 6)
|
||||
){
|
||||
setCurrentState(getCurrentState() + 1)
|
||||
}
|
||||
}
|
||||
|
|
@ -376,7 +395,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||
}
|
||||
}
|
||||
|
||||
if(plantable?.stages ?: 0 > currentGrowthStage && !isGrown()){
|
||||
if((plantable?.stages ?: 0) > currentGrowthStage && !isGrown()){
|
||||
currentGrowthStage++
|
||||
setCurrentState(getCurrentState() + 1)
|
||||
isWatered = false
|
||||
|
|
@ -427,7 +446,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||
|
||||
fun getStageGrowthMinutes() : Int {
|
||||
var minutes = patch.type.stageGrowthTime
|
||||
if(patch.type == PatchType.FRUIT_TREE_PATCH && isGrown()) {
|
||||
if(arrayOf(PatchType.FRUIT_TREE_PATCH, PatchType.CALQUAT_TREE_PATCH).any{ it == patch.type } && isGrown()) {
|
||||
// Fruit trees take 160 minutes per stage to grow, but
|
||||
// restocking their fruit should take 40 minutes per fruit
|
||||
minutes = 40
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ enum class PatchType(val stageGrowthTime: Int) {
|
|||
MUSHROOM_PATCH(30),
|
||||
BELLADONNA_PATCH(80),
|
||||
CACTUS_PATCH(60),
|
||||
EVIL_TURNIP_PATCH(5);
|
||||
EVIL_TURNIP_PATCH(5),
|
||||
CALQUAT_TREE_PATCH(160);
|
||||
|
||||
/**
|
||||
* Returns the display name of this PatchType.
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ enum class Plantable(val itemID: Int, val displayName: String, val value: Int, v
|
|||
MUSHROOM_SPORE(Items.MUSHROOM_SPORE_5282, "mushroom spore", 4, 6, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM_PATCH, Items.MUSHROOM_6004),
|
||||
CACTUS_SEED(Items.CACTUS_SEED_5280, "cactus seed", 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS_PATCH, Items.CACTUS_SPINE_6016),
|
||||
EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, "evil turnip seed", 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP_PATCH, Items.EVIL_TURNIP_12134),
|
||||
CALQUAT_SAPLING(Items.CALQUAT_SAPLING_5503, "calquat tree sapling", 4, 8, 130.5, 48.5, 12096.0, 72, PatchType.CALQUAT_TREE_PATCH, Items.CALQUAT_FRUIT_5980, Item(Items.POISON_IVY_BERRIES_6018, 8)),
|
||||
SPIRIT_SAPLING(Items.SPIRIT_SAPLING_5375, "spirit tree sapling", 8, 12, 199.5, 0.0, 19301.0, 83, PatchType.SPIRIT_TREE_PATCH, 0, Item(Items.MONKEY_NUTS_4012, 5), null, Item(Items.MONKEY_BAR_4014, 1), Item(Items.GROUND_TOOTH_9082, 1) )
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class SackBasketOptionHandler : OptionHandler() {
|
|||
appropriateProduce.amount = (max - specific)
|
||||
}
|
||||
|
||||
if(player.inventory.remove(item) && player.inventory.remove(appropriateProduce))
|
||||
if(player.inventory.remove(appropriateProduce) && player.inventory.remove(item))
|
||||
player.inventory.add(Item(container.containers[specific + appropriateProduce.amount]))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,19 +57,20 @@ class SeedlingListener : InteractionListener {
|
|||
|
||||
fun getSeedling(id: Int) : Int {
|
||||
return when (id) {
|
||||
Items.ACORN_5312 -> Items.OAK_SEEDLING_5358
|
||||
Items.WILLOW_SEED_5313 -> Items.WILLOW_SEEDLING_5359
|
||||
Items.MAPLE_SEED_5314 -> Items.MAPLE_SEEDLING_5360
|
||||
Items.YEW_SEED_5315 -> Items.YEW_SEEDLING_5361
|
||||
Items.MAGIC_SEED_5316 -> Items.MAGIC_SEEDLING_5362
|
||||
Items.APPLE_TREE_SEED_5283 -> Items.APPLE_SEEDLING_5480
|
||||
Items.BANANA_TREE_SEED_5284 -> Items.BANANA_SEEDLING_5481
|
||||
Items.ORANGE_TREE_SEED_5285 -> Items.ORANGE_SEEDLING_5482
|
||||
Items.CURRY_TREE_SEED_5286 -> Items.CURRY_SEEDLING_5483
|
||||
Items.PINEAPPLE_SEED_5287 -> Items.PINEAPPLE_SEEDLING_5484
|
||||
Items.PAPAYA_TREE_SEED_5288 -> Items.PAPAYA_SEEDLING_5485
|
||||
Items.PALM_TREE_SEED_5289 -> Items.PALM_SEEDLING_5486
|
||||
Items.SPIRIT_SEED_5317 -> Items.SPIRIT_SEEDLING_5363
|
||||
Items.ACORN_5312 -> Items.OAK_SEEDLING_5358
|
||||
Items.WILLOW_SEED_5313 -> Items.WILLOW_SEEDLING_5359
|
||||
Items.MAPLE_SEED_5314 -> Items.MAPLE_SEEDLING_5360
|
||||
Items.YEW_SEED_5315 -> Items.YEW_SEEDLING_5361
|
||||
Items.MAGIC_SEED_5316 -> Items.MAGIC_SEEDLING_5362
|
||||
Items.APPLE_TREE_SEED_5283 -> Items.APPLE_SEEDLING_5480
|
||||
Items.BANANA_TREE_SEED_5284 -> Items.BANANA_SEEDLING_5481
|
||||
Items.ORANGE_TREE_SEED_5285 -> Items.ORANGE_SEEDLING_5482
|
||||
Items.CURRY_TREE_SEED_5286 -> Items.CURRY_SEEDLING_5483
|
||||
Items.PINEAPPLE_SEED_5287 -> Items.PINEAPPLE_SEEDLING_5484
|
||||
Items.PAPAYA_TREE_SEED_5288 -> Items.PAPAYA_SEEDLING_5485
|
||||
Items.PALM_TREE_SEED_5289 -> Items.PALM_SEEDLING_5486
|
||||
Items.SPIRIT_SEED_5317 -> Items.SPIRIT_SEEDLING_5363
|
||||
Items.CALQUAT_TREE_SEED_5290 -> Items.CALQUAT_SEEDLING_5487
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +88,8 @@ class SeedlingListener : InteractionListener {
|
|||
Items.PINEAPPLE_SEED_5287,
|
||||
Items.PAPAYA_TREE_SEED_5288,
|
||||
Items.PALM_TREE_SEED_5289,
|
||||
Items.SPIRIT_SEED_5317
|
||||
Items.SPIRIT_SEED_5317,
|
||||
Items.CALQUAT_TREE_SEED_5290
|
||||
)
|
||||
|
||||
val TREE_SEEDLINGS = intArrayOf(
|
||||
|
|
@ -103,7 +105,8 @@ class SeedlingListener : InteractionListener {
|
|||
Items.PINEAPPLE_SEEDLING_5484,
|
||||
Items.PAPAYA_SEEDLING_5485,
|
||||
Items.PALM_SEEDLING_5486,
|
||||
Items.SPIRIT_SEEDLING_5363
|
||||
Items.SPIRIT_SEEDLING_5363,
|
||||
Items.CALQUAT_SEEDLING_5487,
|
||||
)
|
||||
|
||||
private val WATERING_CANS = intArrayOf(Items.WATERING_CAN8_5340,Items.WATERING_CAN7_5339,Items.WATERING_CAN6_5338,Items.WATERING_CAN5_5337,Items.WATERING_CAN4_5336,Items.WATERING_CAN3_5335,Items.WATERING_CAN2_5334,Items.WATERING_CAN1_5333)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import core.plugin.Initializable
|
|||
import core.tools.END_DIALOGUE
|
||||
import core.tools.START_DIALOGUE
|
||||
import org.rs09.consts.Components
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
/**
|
||||
|
|
@ -130,10 +131,9 @@ class ToolLeprechaunOnVacationDialogue(player: Player? = null) : DialoguePlugin(
|
|||
}
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* Goth chatheads are unfortunately updated and have frozen FacialExpressions. Disabled talk-to for now.
|
||||
* This Leprechaun Goth should appear once you have Spirit of Summer done, but is currently defaulted as not.
|
||||
*/
|
||||
// @Initializable
|
||||
@Initializable
|
||||
class ToolLeprechaunGothDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
|
|
@ -178,7 +178,7 @@ class ToolLeprechaunGothDialogue(player: Player? = null) : DialoguePlugin(player
|
|||
}
|
||||
|
||||
override fun newInstance(player: Player?): DialoguePlugin {
|
||||
return ToolLeprechaunDialogue(player)
|
||||
return ToolLeprechaunGothDialogue(player)
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class UseWithPatchHandler : InteractionListener {
|
|||
onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *FarmingPatch.patchNodes.toIntArray()) { player, used, with ->
|
||||
val patch = FarmingPatch.forObject(with.asScenery()) ?: return@onUseWith true
|
||||
val usedItem = used.asItem()
|
||||
val trimmablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH)
|
||||
val trimmablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CALQUAT_TREE_PATCH)
|
||||
|
||||
if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) {
|
||||
if (!hasRequirement(player, Quests.MY_ARMS_BIG_ADVENTURE))
|
||||
|
|
@ -261,7 +261,7 @@ class UseWithPatchHandler : InteractionListener {
|
|||
}*/
|
||||
|
||||
val requiredItem = when (patch.type) {
|
||||
PatchType.TREE_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH -> Items.SPADE_952
|
||||
PatchType.TREE_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CALQUAT_TREE_PATCH -> Items.SPADE_952
|
||||
PatchType.FLOWER_PATCH -> if (plantable == Plantable.SCARECROW) null else Items.SEED_DIBBER_5343
|
||||
else -> Items.SEED_DIBBER_5343
|
||||
}
|
||||
|
|
@ -284,7 +284,8 @@ class UseWithPatchHandler : InteractionListener {
|
|||
}
|
||||
animate(player, anim)
|
||||
playAudio(player, sound)
|
||||
val delay = if (patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.SPIRIT_TREE_PATCH || plantable == Plantable.SCARECROW) 3 else 0
|
||||
val delay = if (patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.SPIRIT_TREE_PATCH ||
|
||||
patch.type == PatchType.CALQUAT_TREE_PATCH || plantable == Plantable.SCARECROW) 3 else 0
|
||||
return@queueScript delayScript(player,anim.duration + delay)
|
||||
}
|
||||
1 -> {
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ class FishingListener : InteractionListener{
|
|||
}
|
||||
return false
|
||||
}
|
||||
return node.isActive && node.location.withinDistance(player.location, 1)
|
||||
return node.isActive && node.location.withinMaxnormDistance(player.location, 1)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ public class SmeltingPulse extends SkillPulse<Item> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if the forging is a succes.
|
||||
* Checks if the forging is a success.
|
||||
*
|
||||
* @param player the player.
|
||||
* @return {@code True} if success.
|
||||
|
|
@ -190,7 +190,9 @@ public class SmeltingPulse extends SkillPulse<Item> {
|
|||
if (charges <= 0) {
|
||||
if (removeItem(player, Items.RING_OF_FORGING_2568, Container.EQUIPMENT)) {
|
||||
charges = 140;
|
||||
sendMessage(player, "Your ring of forging uses up its last charge and disintegrates.");
|
||||
sendMessage(player, "Your Ring of forging uses up its last charge and disintegrates.");
|
||||
// stop the smelting queue
|
||||
stop();
|
||||
} else {
|
||||
log(this.getClass(), Log.ERR, "Failed to delete empty ring of forging for player " + player.getName());
|
||||
return false; //unfair but prevents exploit if the impossible happens
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import static core.api.ContentAPIKt.*;
|
||||
import static core.api.ContentAPIKt.sendMessage;
|
||||
|
||||
/**
|
||||
* Represents a familiar.
|
||||
|
|
@ -258,7 +259,10 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||
if (victim == null) {
|
||||
victim = owner.getAttribute("combat-attacker");
|
||||
}
|
||||
if (combat.getVictim() != this && victim != null && !victim.isInvisible() && getProperties().isMultiZone() && owner.getProperties().isMultiZone() && isCombatFamiliar() && !isBurdenBeast() && !isPeacefulFamiliar()) {
|
||||
if (combat.getVictim() != this && victim != null && !victim.isInvisible()
|
||||
&& getProperties().isMultiZone() && owner.getProperties().isMultiZone()
|
||||
&& victim.getProperties().isMultiZone() && isCombatFamiliar()
|
||||
&& !isBurdenBeast() && !isPeacefulFamiliar()) { // Are you serious my brotha?
|
||||
getProperties().getCombatPulse().attack(victim);
|
||||
}
|
||||
}
|
||||
|
|
@ -271,58 +275,120 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||
}
|
||||
handleFamiliarTick();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAttackable(Entity entity, CombatStyle style, boolean message) {
|
||||
if (isInvalidTarget(entity, style, message)) return false;
|
||||
if (isAttackableBy(entity, style, message)) return false;
|
||||
return super.isAttackable(entity, style, message);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean continueAttack(Entity target, CombatStyle style, boolean message) {
|
||||
return !isInvalidTarget(target, style, message);
|
||||
if (getProperties().getCombatPulse().getVictim() == target) {
|
||||
return canAttackTarget(target, style, message);
|
||||
}
|
||||
if (target == owner) {
|
||||
if (message) {
|
||||
sendMessage(owner, "You can't just betray your own familiar like that!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isInvalidTarget(Entity entity, CombatStyle style, boolean message) {
|
||||
if (entity == owner) {
|
||||
|
||||
/**
|
||||
* Checks if the familiar can attack the target (familiar as attacker).
|
||||
* Called from continueAttack() when familiar initiates combat.
|
||||
*
|
||||
* @param target The target to attack.
|
||||
* @param style The combat style.
|
||||
* @param message Whether to send messages.
|
||||
*
|
||||
* @return true if the familiar can attack the target.
|
||||
*/
|
||||
private boolean canAttackTarget(Entity target, CombatStyle style, boolean message) {
|
||||
if (target == owner) {
|
||||
if (message) {
|
||||
sendMessage(owner, "You can't just betray your own familiar like that!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (target instanceof Player) {
|
||||
Player targetPlayer = (Player) target;
|
||||
if (!targetPlayer.getSkullManager().isWilderness()) {
|
||||
if (message) {
|
||||
sendMessage(owner, "You have to be in the wilderness to attack a player's familiar.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!owner.getSkullManager().isWilderness()) {
|
||||
if (message) {
|
||||
sendMessage(owner, "This familiar's owner is not in the wilderness.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!owner.isAttackable(target, style, false)) {
|
||||
return false;
|
||||
}
|
||||
if (!owner.getProperties().isMultiZone()) {
|
||||
if (message) {
|
||||
sendMessage(owner, "You have to be in a multicombat zone to attack another player with your familiar.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity can attack this familiar (familiar as target).
|
||||
* Called from isAttackable() when someone attacks the familiar.
|
||||
*
|
||||
* @param attacker The entity trying to attack the familiar.
|
||||
* @param style The combat style.
|
||||
* @param message Whether to send messages.
|
||||
*
|
||||
* @return true if the attacker cannot attack the familiar (invalid target).
|
||||
*/
|
||||
private boolean isAttackableBy(Entity attacker, CombatStyle style, boolean message) {
|
||||
if (attacker == owner) {
|
||||
if (message) {
|
||||
sendMessage(owner, "You can't just betray your own familiar like that!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (entity instanceof Player) {
|
||||
if (!owner.isAttackable(entity, style, message)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!getProperties().isMultiZone()) {
|
||||
if (entity instanceof Player && !((Player) entity).getProperties().isMultiZone()) {
|
||||
if (attacker instanceof Player) {
|
||||
Player attackingPlayer = (Player) attacker;
|
||||
if (!attackingPlayer.getSkullManager().isWilderness()) {
|
||||
if (message) {
|
||||
sendMessage((Player) entity, "You have to be in multicombat to attack a player's familiar.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (entity instanceof Player) {
|
||||
if (message) {
|
||||
sendMessage((Player) entity, "This familiar is not in the a multicombat zone.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (entity instanceof Player) {
|
||||
if (!((Player) entity).getSkullManager().isWilderness()) {
|
||||
if (message) {
|
||||
sendMessage((Player) entity, "You have to be in the wilderness to attack a player's familiar.");
|
||||
sendMessage(attackingPlayer, "You have to be in the wilderness to attack a player's familiar.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!owner.getSkullManager().isWilderness()) {
|
||||
if (message) {
|
||||
sendMessage((Player) entity, "This familiar's owner is not in the wilderness.");
|
||||
sendMessage(attackingPlayer, "This familiar's owner is not in the wilderness.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!owner.isAttackable(attackingPlayer, style, false)) {
|
||||
return true;
|
||||
}
|
||||
if (!attackingPlayer.getProperties().isMultiZone()) {
|
||||
if (message) {
|
||||
sendMessage(attackingPlayer, "You have to be in a multicombat zone to attack a player's familiar.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!this.getProperties().isMultiZone()) {
|
||||
if (message) {
|
||||
sendMessage(attackingPlayer, "This familiar is not in a multicombat zone.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -440,9 +506,10 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||
public void visualizeSpecialMove() {
|
||||
owner.visualize(Animation.create(7660), Graphics.create(1316));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a familiar hit.
|
||||
*
|
||||
* @param target the target.
|
||||
* @param maxHit the max hit.
|
||||
* @param graphics the graphics.
|
||||
|
|
@ -459,6 +526,13 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||
hit = RandomFunction.randomize(maxHit);
|
||||
}
|
||||
state.setEstimatedHit(hit);
|
||||
if (target instanceof NPC && ((NPC) target).getTask() != null && ((NPC) target).getTask().levelReq > owner.getSkills().getLevel(Skills.SLAYER)) {
|
||||
state.neutralizeHits();
|
||||
state.setPoisonDamage(-1);
|
||||
if (state.getStyle() == CombatStyle.MAGIC) {
|
||||
state.setEstimatedHit(-1);
|
||||
}
|
||||
}
|
||||
target.getImpactHandler().handleImpact(owner, hit, CombatStyle.MELEE, state);
|
||||
if (graphics != null) {
|
||||
target.graphics(graphics);
|
||||
|
|
@ -484,7 +558,7 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||
public void sendFamiliarHit(final Entity target, final int maxHit) {
|
||||
sendFamiliarHit(target, maxHit, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if this familiar can attack the target (used mainly for special
|
||||
* moves).
|
||||
|
|
@ -495,11 +569,17 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||
}
|
||||
if (target.getLocation().getDistance(getLocation()) > 8) {
|
||||
if (message) {
|
||||
owner.getPacketDispatch().sendMessage("That target is too far.");
|
||||
sendMessage(owner, "That target is too far.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (target.getLocks().isInteractionLocked() || !target.isAttackable(this, CombatStyle.MAGIC, true)) {
|
||||
if (target instanceof Player && !owner.getProperties().isMultiZone()) {
|
||||
if (message) {
|
||||
sendMessage(owner, "You have to be in a multicombat zone to attack another player with your familiar.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (target.getLocks().isInteractionLocked() || !target.isAttackable(this, CombatStyle.MAGIC, message)) {
|
||||
return false;
|
||||
}
|
||||
return isCombatFamiliar();
|
||||
|
|
@ -509,11 +589,13 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||
public boolean canAttack(Entity target) {
|
||||
return canAttack(target, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a familiar can perform a combat special attack.
|
||||
*
|
||||
* @param target the target.
|
||||
* @param message show message.
|
||||
*
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean canCombatSpecial(Entity target, boolean message) {
|
||||
|
|
@ -526,6 +608,10 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||
if (getAttribute("special-delay", 0) > GameWorld.getTicks()) {
|
||||
return false;
|
||||
}
|
||||
// The special move probably should go through but with all harmful effects neutralized, but for now just block the damn thing
|
||||
if (target instanceof NPC && ((NPC) target).getTask() != null && ((NPC) target).getTask().levelReq > owner.getSkills().getLevel(Skills.SLAYER)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class IbisNPC extends Forager {
|
|||
* @param id The id.
|
||||
*/
|
||||
public IbisNPC(Player owner, int id) {
|
||||
super(owner, id, 3800, 12531, 12, new Item(361), new Item(373));
|
||||
super(owner, id, 3800, 12531, 12, new Item(359), new Item(371));
|
||||
boosts.add(new SkillBonus(Skills.FISHING, 3));
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ public class IbisNPC extends Forager {
|
|||
@Override
|
||||
public boolean produceItem(Item item) {
|
||||
if (super.produceItem(item)) {
|
||||
if (item.getId() == 373) {
|
||||
if (item.getId() == 371) {
|
||||
owner.getSkills().addExperience(Skills.FISHING, 10);
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package content.global.skill.summoning.familiar;
|
|||
import java.util.List;
|
||||
|
||||
import core.cache.def.impl.NPCDefinition;
|
||||
import core.game.node.entity.combat.equipment.WeaponInterface;
|
||||
import core.plugin.Initializable;
|
||||
import core.game.interaction.OptionHandler;
|
||||
import core.game.node.Node;
|
||||
|
|
@ -36,7 +37,7 @@ public class SmokeDevilNPC extends Familiar {
|
|||
* @param id The id.
|
||||
*/
|
||||
public SmokeDevilNPC(Player owner, int id) {
|
||||
super(owner, id, 4800, 12085, 6);
|
||||
super(owner, id, 4800, 12085, 6, WeaponInterface.STYLE_CAST);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -2,36 +2,35 @@ package content.minigame.allfiredup
|
|||
|
||||
import core.api.*
|
||||
import core.game.node.entity.player.Player
|
||||
import core.tools.SystemLogger
|
||||
import core.game.world.map.Location
|
||||
import core.tools.Log
|
||||
|
||||
/**
|
||||
* Various data for beacons, such as varp and offset, required FM level, etc
|
||||
* @author Ceikry
|
||||
* @author Ceikry, Player Name
|
||||
*/
|
||||
enum class AFUBeacon(val title: String, val fmLevel: Int, val varbit: Int, val location: Location, val experience: Double, val keeper: Int = 0) {
|
||||
RIVER_SALVE("",43,5146,Location.create(3396, 3464, 0),216.2,8065),
|
||||
RAG_AND_BONE("",43,5147,Location.create(3343, 3510, 0),235.8,8066),
|
||||
JOLLY_BOAR("",48,5148,Location.create(3278, 3525, 0), 193.8,8067),
|
||||
NORTH_VARROCK_CASTLE("",53,5149,Location.create(3236, 3527, 0),178.5,8068),
|
||||
GRAND_EXCHANGE("",59,5150,Location.create(3170, 3536, 0),194.3,8069),
|
||||
EDGEVILLE("",62,5151,Location.create(3087, 3516, 0),86.7,8070),
|
||||
MONASTERY("",68,5152,Location.create(3034, 3518, 0),224.4,8071),
|
||||
GOBLIN_VILLAGE("",72,5153,Location.create(2968, 3516, 0),194.8,8072),
|
||||
BURTHORPE("",76,5154,Location.create(2940, 3565, 0),195.3,8073),
|
||||
DEATH_PLATEAU("",79,5155,Location.create(2944, 3622, 0),249.9,8074),
|
||||
TROLLHEIM("",83,5156,Location.create(2939, 3680, 0),201.0,8075),
|
||||
GWD("",87,5157,Location.create(2937, 3773, 0),255.0,8076),
|
||||
TEMPLE("",89,5158,Location.create(2946, 3836, 0),198.9),
|
||||
PLATEAU("",92,5159,Location.create(2964, 3931, 0),147.9);
|
||||
RIVER_SALVE("near the River Salve", 43, 5146, Location.create(3396, 3464, 0), 216.2, 8065),
|
||||
RAG_AND_BONE("near the Rag and Bone Man", 43, 5147, Location.create(3343, 3510, 0), 235.8, 8066),
|
||||
JOLLY_BOAR("north of the Jolly Boar", 48, 5148, Location.create(3278, 3525, 0), 193.8, 8067),
|
||||
NORTH_VARROCK_CASTLE("north of Varrock Palace", 53, 5149, Location.create(3236, 3527, 0), 178.5, 8068),
|
||||
GRAND_EXCHANGE("north of the Grand Exchange", 59, 5150, Location.create(3170, 3536, 0), 194.3, 8069),
|
||||
EDGEVILLE("near Edgeville", 62, 5151, Location.create(3087, 3516, 0), 86.7, 8070),
|
||||
MONASTERY("near the Black Knights' Fortress", 68, 5152, Location.create(3034, 3518, 0), 224.4, 8071),
|
||||
GOBLIN_VILLAGE("near Goblin Village", 72, 5153, Location.create(2968, 3516, 0), 194.8, 8072),
|
||||
BURTHORPE("near Burthorpe", 76, 5154, Location.create(2940, 3565, 0), 195.3, 8073),
|
||||
DEATH_PLATEAU("east of Death Plateau", 79, 5155, Location.create(2944, 3622, 0), 249.9, 8074),
|
||||
TROLLHEIM("east of Trollheim", 83, 5156, Location.create(2939, 3680, 0), 201.0, 8075),
|
||||
GWD("east of the God Wars dungeon temple", 87, 5157, Location.create(2937, 3773, 0), 255.0, 8076),
|
||||
TEMPLE("north of the small temple in the Wilderness", 89, 5158, Location.create(2946, 3836, 0), 198.9),
|
||||
PLATEAU("on the Frozen Waste Plateau", 92, 5159, Location.create(2964, 3931, 0), 147.9);
|
||||
|
||||
companion object {
|
||||
fun forLocation(location: Location): AFUBeacon {
|
||||
for (beacon in values()) {
|
||||
if (beacon.location.equals(location)) return beacon
|
||||
}
|
||||
return AFUBeacon.RIVER_SALVE.also { log(this::class.java, Log.WARN, "Unhandled Beacon Location ${location.toString()}") }
|
||||
return AFUBeacon.RIVER_SALVE.also { log(this::class.java, Log.WARN, "Unhandled Beacon Location ${location.toString()}") }
|
||||
}
|
||||
|
||||
fun resetAllBeacons(player: Player){
|
||||
|
|
@ -42,19 +41,19 @@ enum class AFUBeacon(val title: String, val fmLevel: Int, val varbit: Int, val l
|
|||
}
|
||||
|
||||
fun light(player: Player){
|
||||
setVarbit(player, varbit, 2)
|
||||
setVarbit(player, varbit, 2, true)
|
||||
}
|
||||
|
||||
fun diminish(player: Player){
|
||||
setVarbit(player, varbit, 3)
|
||||
setVarbit(player, varbit, 3, true)
|
||||
}
|
||||
|
||||
fun extinguish(player: Player){
|
||||
setVarbit(player, varbit, 0)
|
||||
setVarbit(player, varbit, 0, true)
|
||||
}
|
||||
|
||||
fun lightGnomish(player: Player){
|
||||
setVarbit(player, varbit, 4)
|
||||
setVarbit(player, varbit, 4, true)
|
||||
}
|
||||
|
||||
fun fillWithLogs(player: Player){
|
||||
|
|
|
|||
|
|
@ -4,13 +4,23 @@ import core.game.node.entity.npc.NPC
|
|||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import org.rs09.consts.Items
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import core.game.world.GameWorld
|
||||
import content.data.Quests
|
||||
import core.api.delayScript
|
||||
import core.api.getAttribute
|
||||
import core.api.getOrStartTimer
|
||||
import core.api.getQuestStage
|
||||
import core.api.lock
|
||||
import core.api.queueScript
|
||||
import core.api.removeAttribute
|
||||
import core.api.sendMessage
|
||||
import core.api.setAttribute
|
||||
import core.api.setQuestStage
|
||||
import core.api.stopExecuting
|
||||
import core.game.interaction.QueueStrength
|
||||
|
||||
private val VALID_LOGS = intArrayOf(Items.LOGS_1511, Items.OAK_LOGS_1521,Items.WILLOW_LOGS_1519,Items.MAPLE_LOGS_1517,Items.YEW_LOGS_1515,Items.MAGIC_LOGS_1513)
|
||||
private val FILL_ANIM = Animation(9136)
|
||||
|
|
@ -18,7 +28,7 @@ private val LIGHT_ANIM = Animation(7307)
|
|||
|
||||
/**
|
||||
* Handles interactions for beacons
|
||||
* @author Ceikry
|
||||
* @author Ceikry, Player Name
|
||||
*/
|
||||
class AFUBeaconListeners : InteractionListener {
|
||||
|
||||
|
|
@ -86,112 +96,80 @@ class AFUBeaconListeners : InteractionListener {
|
|||
|
||||
val logs = getLogs(player,20)
|
||||
if (logs.id != 0 && player.inventory.remove(logs)) {
|
||||
player.lock()
|
||||
|
||||
var session: AFUSession? = null
|
||||
if(questComplete){
|
||||
session = player.getAttribute("afu-session", null)
|
||||
if(session == null) {
|
||||
session = AFUSession(player)
|
||||
session.init()
|
||||
lock(player, FILL_ANIM.duration)
|
||||
queueScript(player, 0, QueueStrength.SOFT) { stage ->
|
||||
if (stage == 1) {
|
||||
player.animator.animate(FILL_ANIM)
|
||||
return@queueScript delayScript(player, FILL_ANIM.duration)
|
||||
}
|
||||
setAttribute(player, "/save:beacon:${beacon.ordinal}:logsId", logs.id)
|
||||
beacon.fillWithLogs(player)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
|
||||
GameWorld.Pulser.submit(object : Pulse() {
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when (counter++) {
|
||||
0 -> player.animator.animate(FILL_ANIM)
|
||||
1 -> {
|
||||
beacon.fillWithLogs(player)
|
||||
if(questComplete){
|
||||
session?.setLogs(beacon.ordinal,logs)
|
||||
}
|
||||
}
|
||||
2 -> player.unlock().also {player.animator.animate(Animation.RESET); return true }
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
player.dialogueInterpreter.sendDialogue("You need some logs to do this.")
|
||||
}
|
||||
}
|
||||
|
||||
fun lightBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean){
|
||||
var session: AFUSession? = null
|
||||
if(questComplete){
|
||||
session = player.getAttribute("afu-session",null)
|
||||
if(session == null) return
|
||||
}
|
||||
|
||||
fun lightBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean) {
|
||||
if(player.inventory.contains(Items.TINDERBOX_590,1)){
|
||||
player.lock()
|
||||
GameWorld.Pulser.submit(object: Pulse(){
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when(counter++){
|
||||
0 -> player.animator.animate(LIGHT_ANIM)
|
||||
1 -> {
|
||||
beacon.light(player)
|
||||
if(questComplete){
|
||||
session?.startTimer(beacon.ordinal)
|
||||
if(session?.getLitBeacons() == 6 && !player.hasFireRing()){
|
||||
player.sendMessage("Congratulations on lighting 6 beacons at once! King Roald has something for you.")
|
||||
player.setAttribute("/save:afu-mini:ring",true)
|
||||
}
|
||||
if(session?.getLitBeacons() == 10 && !player.hasFlameGloves()){
|
||||
player.sendMessage("Congratulations on lighting 10 beacons at once! King Roald has something for you.")
|
||||
player.setAttribute("/save:afu-mini:gloves",true)
|
||||
}
|
||||
if(session?.getLitBeacons() == 14 && !player.hasInfernoAdze()){
|
||||
player.sendMessage("Congratulations on lighting all 14 beacons! King Roald has something special for you.")
|
||||
player.setAttribute("/save:afu-mini:adze",true)
|
||||
}
|
||||
var experience = beacon.experience
|
||||
experience += session?.getBonusExperience() ?: 0.0
|
||||
player.skills.addExperience(Skills.FIREMAKING,experience)
|
||||
} else {
|
||||
player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player, player.questRepository.getStage(Quests.ALL_FIRED_UP) + 10)
|
||||
}
|
||||
}
|
||||
2 -> player.unlock().also { return true }
|
||||
}
|
||||
return false
|
||||
lock(player, LIGHT_ANIM.duration)
|
||||
queueScript(player, 0, QueueStrength.SOFT) { stage ->
|
||||
if (stage == 1) {
|
||||
player.animator.animate(LIGHT_ANIM)
|
||||
return@queueScript delayScript(player, LIGHT_ANIM.duration)
|
||||
}
|
||||
})
|
||||
val logsId = getAttribute(player, "/save:beacon:${beacon.ordinal}:logsId", Items.LOGS_1511)
|
||||
removeAttribute(player, "/save:beacon:${beacon.ordinal}:logsId")
|
||||
beacon.light(player)
|
||||
if (!questComplete) {
|
||||
setQuestStage(player, Quests.ALL_FIRED_UP, getQuestStage(player, Quests.ALL_FIRED_UP) + 10)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
val timer = getOrStartTimer<AFUTimer>(player)
|
||||
timer.addTime(beacon.ordinal, logsId, 20)
|
||||
val lit = timer.getLitBeacons()
|
||||
if (lit == 6 && !player.hasFireRing()) {
|
||||
sendMessage(player, "Congratulations on lighting 6 beacons at once! King Roald has something for you.")
|
||||
setAttribute(player, "/save:afu-mini:ring", true)
|
||||
}
|
||||
if (lit == 10 && !player.hasFlameGloves()) {
|
||||
sendMessage(player, "Congratulations on lighting 10 beacons at once! King Roald has something for you.")
|
||||
setAttribute(player, "/save:afu-mini:gloves", true)
|
||||
}
|
||||
if (lit == 14 && !player.hasInfernoAdze()) {
|
||||
sendMessage(player, "Congratulations on lighting all 14 beacons! King Roald has something special for you.")
|
||||
setAttribute(player, "/save:afu-mini:adze", true)
|
||||
}
|
||||
val xp = beacon.experience + timer.getBonusExperience()
|
||||
player.skills.addExperience(Skills.FIREMAKING, xp)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
} else {
|
||||
player.dialogueInterpreter.sendDialogue("You need a tinderbox to light this.")
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean){
|
||||
var session: AFUSession? = null
|
||||
if(questComplete){
|
||||
session = player.getAttribute("afu-session",null)
|
||||
if(session == null) return
|
||||
}
|
||||
|
||||
val logs = getLogs(player, 5)
|
||||
if (logs.id != 0 && player.inventory.remove(logs)) {
|
||||
player.lock()
|
||||
GameWorld.Pulser.submit(object: Pulse(){
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when(counter++){
|
||||
0 -> player.animator.animate(FILL_ANIM)
|
||||
1 -> beacon.light(player).also {
|
||||
if(questComplete){
|
||||
session?.refreshTimer(beacon,logs.id)
|
||||
} else {
|
||||
player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player, 80)
|
||||
}
|
||||
}
|
||||
2 -> player.unlock().also { return true }
|
||||
}
|
||||
return false
|
||||
lock(player, FILL_ANIM.duration)
|
||||
queueScript(player, 0, QueueStrength.SOFT) { stage ->
|
||||
if (stage == 1) {
|
||||
player.animator.animate(LIGHT_ANIM)
|
||||
return@queueScript delayScript(player, LIGHT_ANIM.duration)
|
||||
}
|
||||
})
|
||||
val backupLogsId = getAttribute(player, "/save:beacon:${beacon.ordinal}:backupLogsId", Items.LOGS_1511)
|
||||
removeAttribute(player, "/save:beacon:${beacon.ordinal}:backupLogsId")
|
||||
beacon.light(player)
|
||||
if (!questComplete) {
|
||||
setQuestStage(player, Quests.ALL_FIRED_UP, 80)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
val timer = getOrStartTimer<AFUTimer>(player)
|
||||
timer.addTime(beacon.ordinal, backupLogsId, 5)
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
} else {
|
||||
player.dialogueInterpreter.sendDialogue("You need some logs to do this.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
package content.minigame.allfiredup
|
||||
|
||||
import core.api.LogoutListener
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.GameWorld
|
||||
import org.rs09.consts.Items
|
||||
import core.tools.colorize
|
||||
|
||||
/**
|
||||
* Handles keeping track of lit beacons and their burn time remaining
|
||||
* @author Ceikry
|
||||
*/
|
||||
class AFUSession(val player: Player? = null) : LogoutListener {
|
||||
private val beaconTimers = Array(14){i -> BeaconTimer(0, AFUBeacon.values()[i]) }
|
||||
private val logInventories = Array(14){Item(0,0)}
|
||||
private val beaconWatched = Array(14){false}
|
||||
private var isActive = false
|
||||
|
||||
fun init() {
|
||||
isActive = true
|
||||
GameWorld.Pulser.submit(object: Pulse(){
|
||||
override fun pulse(): Boolean {
|
||||
player!!.setAttribute("afu-pulse",this)
|
||||
beaconTimers.forEach {timer ->
|
||||
timer.ticks--
|
||||
if(timer.ticks == 300) timer.beacon.diminish(player).also {
|
||||
if(beaconWatched[timer.beacon.ordinal]){
|
||||
beaconWatched[timer.beacon.ordinal] = false
|
||||
timer.ticks += (getTicks(logInventories[timer.beacon.ordinal].id) * 5)
|
||||
timer.beacon.light(player)
|
||||
player.sendMessage(colorize("%RThe ${timer.beacon.name.toLowerCase().replace("_"," ")} watcher has used your backup logs."))
|
||||
} else {
|
||||
player.sendMessage(colorize("%RThe ${timer.beacon.name.toLowerCase().replace("_", " ")} beacon is dying!"))
|
||||
}
|
||||
}
|
||||
if(timer.ticks == 0) timer.beacon.extinguish(player).also { player.sendMessage(colorize("%RThe ${timer.beacon.name.toLowerCase().replace("_"," ")} beacon has gone out!")) }
|
||||
}
|
||||
return !isActive
|
||||
}
|
||||
})
|
||||
player!!.setAttribute("afu-session",this)
|
||||
}
|
||||
|
||||
fun getLitBeacons(): Int{
|
||||
return beaconTimers.count { it.ticks > 0 }
|
||||
}
|
||||
|
||||
fun end(){
|
||||
isActive = false
|
||||
}
|
||||
|
||||
fun setLogs(beaconIndex: Int, logs: Item){
|
||||
logInventories[beaconIndex] = logs
|
||||
}
|
||||
|
||||
fun startTimer(beaconIndex: Int){
|
||||
val ticks = getTicks(logInventories[beaconIndex].id) * 20
|
||||
logInventories[beaconIndex] = Item(0,0)
|
||||
beaconTimers[beaconIndex].ticks = ticks
|
||||
}
|
||||
|
||||
fun refreshTimer(beacon: AFUBeacon, logID: Int){
|
||||
val ticks = getTicks(logID) * 5
|
||||
beaconTimers.forEach {
|
||||
if(it.beacon.ordinal == beacon.ordinal) it.ticks += ticks
|
||||
}
|
||||
}
|
||||
|
||||
fun setWatcher(index: Int, logs: Item){
|
||||
beaconWatched[index] = true
|
||||
logInventories[index] = logs
|
||||
}
|
||||
|
||||
fun isWatched(index: Int): Boolean{
|
||||
return beaconWatched[index]
|
||||
}
|
||||
|
||||
fun getTicks(logID: Int): Int{
|
||||
val ticks = when(logID){
|
||||
Items.LOGS_1511 -> 65
|
||||
Items.OAK_LOGS_1521 -> 68
|
||||
Items.WILLOW_LOGS_1519 -> 73
|
||||
Items.MAPLE_LOGS_1517 -> 79
|
||||
Items.YEW_LOGS_1515 -> 83
|
||||
Items.MAGIC_LOGS_1513 -> 90
|
||||
else -> 0
|
||||
}
|
||||
return ticks
|
||||
}
|
||||
|
||||
fun getBonusExperience(): Double{
|
||||
return when(getLitBeacons()){
|
||||
1 -> 608.4
|
||||
2 -> 1622.4
|
||||
3 -> 1987.4
|
||||
4 -> 2149.6
|
||||
5 -> 2149.6
|
||||
6 -> 2514.6
|
||||
7 -> 2555.2
|
||||
8 -> 2758.0
|
||||
9 -> 2839.1
|
||||
10 -> 3041.9
|
||||
11 -> 3123.0
|
||||
12 -> 3244.7
|
||||
13 -> 3366.4
|
||||
14 -> 4867.4
|
||||
else -> 0.0
|
||||
}
|
||||
}
|
||||
|
||||
override fun logout(player: Player) {
|
||||
AFUBeacon.resetAllBeacons(player)
|
||||
val session: AFUSession? = player.getAttribute("afu-session",null)
|
||||
session?.end()
|
||||
player.removeAttribute("afu-session")
|
||||
}
|
||||
|
||||
internal class BeaconTimer(var ticks: Int, val beacon: AFUBeacon)
|
||||
}
|
||||
104
Server/src/main/content/minigame/allfiredup/AFUTimer.kt
Normal file
104
Server/src/main/content/minigame/allfiredup/AFUTimer.kt
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
package content.minigame.allfiredup
|
||||
|
||||
import core.api.getAttribute
|
||||
import core.api.log
|
||||
import core.api.removeAttribute
|
||||
import core.api.sendMessage
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.system.timer.PersistTimer
|
||||
import core.tools.Log
|
||||
import core.tools.colorize
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class AFUTimer : PersistTimer(1, "AFU timer", isSoft = true) {
|
||||
private val burnTicks = Array(14) { 0 }
|
||||
|
||||
fun addTime(index: Int, logsId: Int, numLogs: Int) {
|
||||
val ticksPerLog = when (logsId) {
|
||||
Items.LOGS_1511 -> 65
|
||||
Items.OAK_LOGS_1521 -> 68
|
||||
Items.WILLOW_LOGS_1519 -> 73
|
||||
Items.MAPLE_LOGS_1517 -> 79
|
||||
Items.YEW_LOGS_1515 -> 83
|
||||
Items.MAGIC_LOGS_1513 -> 90
|
||||
else -> 0
|
||||
}
|
||||
burnTicks[index] += numLogs * ticksPerLog
|
||||
}
|
||||
|
||||
fun getBonusExperience(): Double {
|
||||
return when (getLitBeacons()) {
|
||||
1 -> 608.4
|
||||
2 -> 1622.4
|
||||
3 -> 1987.4
|
||||
4 -> 2149.6
|
||||
5 -> 2149.6
|
||||
6 -> 2514.6
|
||||
7 -> 2555.2
|
||||
8 -> 2758.0
|
||||
9 -> 2839.1
|
||||
10 -> 3041.9
|
||||
11 -> 3123.0
|
||||
12 -> 3244.7
|
||||
13 -> 3366.4
|
||||
14 -> 4867.4
|
||||
else -> 0.0
|
||||
}
|
||||
}
|
||||
|
||||
fun getLitBeacons(): Int {
|
||||
return burnTicks.count { it > 0 }
|
||||
}
|
||||
|
||||
override fun save(root: JSONObject, entity: Entity) {
|
||||
val beaconArray = JSONArray()
|
||||
for (i in 0..13) {
|
||||
beaconArray.add(burnTicks[i])
|
||||
}
|
||||
root["beacons"] = beaconArray
|
||||
}
|
||||
|
||||
override fun parse(root: JSONObject, entity: Entity) {
|
||||
val beacons = root["beacons"] as JSONArray
|
||||
for (i in 0..13) {
|
||||
burnTicks[i] = beacons[i].toString().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
override fun run(entity: Entity) : Boolean {
|
||||
if (entity !is Player) {
|
||||
log(this::class.java, Log.ERR, "How did a non-Player light an AFU beacon?")
|
||||
return false
|
||||
}
|
||||
val player = entity
|
||||
var keepRunning = false
|
||||
for (i in 0..13) {
|
||||
val beacon = AFUBeacon.values()[i]
|
||||
if (burnTicks[i] > 0) {
|
||||
burnTicks[i]--
|
||||
if (burnTicks[i] > 0) {
|
||||
keepRunning = true
|
||||
}
|
||||
if (burnTicks[i] == 300) {
|
||||
val backupLogsId = getAttribute(player, "/save:beacon:$i:backupLogsId", 0)
|
||||
if (backupLogsId != 0) {
|
||||
addTime(i, backupLogsId, 5)
|
||||
removeAttribute(player, "/save:beacon:$i:backupLogsId")
|
||||
sendMessage(player, colorize("%RThe beacon watcher ${beacon.title} has used your backup logs."))
|
||||
} else {
|
||||
beacon.diminish(player)
|
||||
sendMessage(player, colorize("%RThe beacon ${beacon.title} is dying!"))
|
||||
}
|
||||
} else if (burnTicks[i] == 0) {
|
||||
beacon.extinguish(player)
|
||||
removeAttribute(player, "/save:beacon:$i:logsId")
|
||||
sendMessage(player, colorize("%RThe beacon ${beacon.title} has gone out!"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return keepRunning
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package content.minigame.allfiredup
|
||||
|
||||
import core.ServerConstants
|
||||
import core.game.dialogue.*
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.item.Item
|
||||
import core.plugin.Initializable
|
||||
import content.region.wilderness.dialogue.*
|
||||
import core.api.getAttribute
|
||||
import core.api.setAttribute
|
||||
import org.rs09.consts.Items
|
||||
|
||||
private val VALID_LOGS = arrayOf(Items.LOGS_1511, Items.OAK_LOGS_1521, Items.WILLOW_LOGS_1519, Items.MAPLE_LOGS_1517, Items.YEW_LOGS_1515, Items.MAGIC_LOGS_1513)
|
||||
|
|
@ -41,10 +41,9 @@ class BeaconTenderDialogue(player: Player? = null) : DialoguePlugin(player) {
|
|||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
val beacon = AFUBeacon.values()[index]
|
||||
val logs = getLogs(player,5)
|
||||
val session: AFUSession? = player.getAttribute("afu-session",null)
|
||||
when(stage){
|
||||
0 -> player("Hello!").also { stage++ }
|
||||
1 -> if(beacon.getState(player) == BeaconState.LIT && session?.isWatched(index) == false){
|
||||
1 -> if (beacon.getState(player) == BeaconState.LIT && getAttribute(player, "/save:beacon:${beacon.ordinal}:backupLogsId", 0) == 0) {
|
||||
options("Can you watch this beacon for me?","Nevermind.").also { stage = 10 }
|
||||
} else {
|
||||
npc("Carry on, adventurer.").also { stage = 1000 }
|
||||
|
|
@ -62,7 +61,7 @@ class BeaconTenderDialogue(player: Player? = null) : DialoguePlugin(player) {
|
|||
13 -> npc("Great, hand them over.").also { stage++ }
|
||||
14 -> player("Here you go!").also {
|
||||
player.inventory.remove(logs)
|
||||
session?.setWatcher(index,logs)
|
||||
setAttribute(player, "/save:beacon:${beacon.ordinal}:backupLogsId", logs.id)
|
||||
stage = 1000
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -129,8 +130,9 @@ class WitchesDiaryBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.DIARY_2408, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.DIARY_2408, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ import core.game.node.entity.combat.CombatStyle;
|
|||
import core.game.node.entity.combat.CombatSwingHandler;
|
||||
import core.game.node.entity.combat.InteractionType;
|
||||
import core.game.node.entity.combat.equipment.ArmourSet;
|
||||
import core.game.node.entity.impl.Projectile;
|
||||
import core.game.node.entity.impl.Animator.Priority;
|
||||
import core.game.node.entity.impl.Projectile;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.game.world.update.flag.context.Graphics;
|
||||
import core.tools.RandomFunction;
|
||||
|
||||
import static core.api.ContentAPIKt.*;
|
||||
import static core.api.ContentAPIKt.applyPoison;
|
||||
|
||||
/**
|
||||
* Handles K'ril Tsutsaroth's combat.
|
||||
|
|
@ -62,11 +62,13 @@ public final class GWDTsutsarothSwingHandler extends CombatSwingHandler {
|
|||
if (RandomFunction.randomize(10) < 4) {
|
||||
ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3);
|
||||
style = CombatStyle.MAGIC;
|
||||
} else if (RandomFunction.randomize(10) == 0) {
|
||||
if (special = (victim instanceof Player)) {
|
||||
((Player) victim).getPacketDispatch().sendMessage("K'ril Tsutsaroth slams through your protection prayer, leaving you feeling drained.");
|
||||
}
|
||||
} else if (victim instanceof Player && RandomFunction.randomize(10) == 0) {
|
||||
Player player = (Player) victim;
|
||||
special = player.hasProtectionPrayer(CombatStyle.MELEE);
|
||||
if (special) {
|
||||
player.getPacketDispatch().sendMessage("K'ril Tsutsaroth slams through your protection prayer, leaving you feeling drained.");
|
||||
entity.sendChat("YARRRRRRR!");
|
||||
}
|
||||
}
|
||||
if (style.getSwingHandler().isAccurateImpact(entity, victim)) {
|
||||
int max = style.getSwingHandler().calculateHit(entity, victim, special ? 1.08 : 1.0);
|
||||
|
|
@ -76,7 +78,7 @@ public final class GWDTsutsarothSwingHandler extends CombatSwingHandler {
|
|||
applyPoison(victim, entity, 80);
|
||||
}
|
||||
if (special) {
|
||||
((Player) victim).getSkills().decrementPrayerPoints((double) hit / 2);
|
||||
victim.getSkills().decrementPrayerPoints((double) hit / 2);
|
||||
}
|
||||
}
|
||||
state.setEstimatedHit(hit);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import content.global.handlers.iface.BookInterface
|
|||
import content.global.handlers.iface.BookLine
|
||||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -177,8 +178,9 @@ class TranslationBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.TRANSLATION_4655, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.TRANSLATION_4655, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -288,8 +289,9 @@ class VarmensNotes : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.VARMENS_NOTES_4616, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.VARMENS_NOTES_4616, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public class SophanemPlugin extends OptionHandler {
|
|||
SceneryDefinition.forId(20275).getHandlers().put("option:climb-down", this);
|
||||
SceneryDefinition.forId(20391).getHandlers().put("option:open", this);
|
||||
SceneryDefinition.forId(28514).getHandlers().put("option:open", this);
|
||||
SceneryDefinition.forId(20341).getHandlers().put("option:open", this);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +54,8 @@ public class SophanemPlugin extends OptionHandler {
|
|||
}
|
||||
DoorActionHandler.handleDoor(player, (Scenery) node);
|
||||
break;
|
||||
case 20341:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.HAZEEL_SCROLL
|
||||
import core.api.*
|
||||
import core.game.dialogue.*
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.map.Location
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class AlomoneDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
val hazeel = HazeelNPC(NPCs.HAZEEL_892, Location.create(2607, 9669, 0))
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int, ): Boolean {
|
||||
|
||||
val questStage = getQuestStage(player, Quests.HAZEEL_CULT)
|
||||
|
||||
when {
|
||||
// you can't access Alomone before stage 2
|
||||
// stage 2 - talk to clivet (set attr for carnillean)
|
||||
// stage 2 - talk to clivet (set attr for mahjarrat)
|
||||
// stage 3 - poison poured in food (mahjarrat-only stage)
|
||||
(questStage in 2..3) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hi there.").also { stage = 20 }
|
||||
} else {
|
||||
npcl(FacialExpression.ANGRY, "How did YOU get in here?").also { stage ++}
|
||||
}
|
||||
}
|
||||
// carnillean arc
|
||||
1 -> playerl(FacialExpression.NEUTRAL, "I've come for the Carnillean family armour. Hand it over, or face the consequences.").also { stage++ }
|
||||
2 -> npcl(FacialExpression.ANNOYED, "I thought I made it clear to the butler you could not be allowed to interfere with our mission. The incompetent fool must be going soft.").also { stage++ }
|
||||
3 -> playerl(FacialExpression.NEUTRAL, "So, the butler's part of your sordid little cult, huh? Why is it ALWAYS the butler? I should have known...").also { stage++ }
|
||||
4 -> npcl(FacialExpression.ANNOYED, "Well, you won't live long enough to tell anyone! DIE!!!").also { stage++ }
|
||||
5 -> {
|
||||
end()
|
||||
setQuestStage(player, Quests.HAZEEL_CULT, 4)
|
||||
npc!!.attack(player!!)
|
||||
}
|
||||
|
||||
// mahjarrat arc
|
||||
20 -> npcl(FacialExpression.NEUTRAL, "Well well well... So we have a new recruit. Clivet told me of your desire to join us in our glorious task to resurrect the mighty Hazeel from his slumber.").also { stage++ }
|
||||
21 -> npcl(FacialExpression.NEUTRAL, "To accomplish this, we require the ancient words of summoning which will restore his shattered body, so that he can bring vengeance upon his enemies.").also { stage++ }
|
||||
22 -> npcl(FacialExpression.NEUTRAL, "Hazeel in his mighty cunning anticipated that he might be defeated and secured within his home somewhere a powerful magical scroll that could restore him.").also { stage++ }
|
||||
23 -> npcl(FacialExpression.NEUTRAL, "The words to this powerful enchantment are hidden within the Carnillean mansion, right under the nose of his unsuspecting foes. We already have an agent in place there").also { stage++}
|
||||
24 -> npcl(FacialExpression.NEUTRAL, "in their Butler Jones, a faithful follower of Hazeel. His loyalty to Hazeel is beyond questioning, yet he has been unsuccessful in locating the scroll containing the spell so far.").also { stage++ }
|
||||
25 -> npcl(FacialExpression.NEUTRAL, "Go back to the mansion, and assist us in finding this enchantment, so that together we can restore Hazeel to his true power and glory!").also { stage = END_DIALOGUE}.also { setQuestStage(player, Quests.HAZEEL_CULT, 4) }
|
||||
}
|
||||
|
||||
// stage 4 - alomone either fought or he tells you he needs scroll
|
||||
// stage 5 - either returning the armour, or finding the scroll
|
||||
(questStage in 4..5 && mahjarratArc(player)) -> when (stage) {
|
||||
0 -> if(npc.id == NPCs.ALOMONE_891 && !getAttribute(player, "hazeelSpawned", false)) {
|
||||
playerl(FacialExpression.NEUTRAL, "Hi.").also { stage++ }
|
||||
} else {
|
||||
playerl(FacialExpression.NEUTRAL, "Hi.").also { stage = 12 }
|
||||
}
|
||||
1 -> npcl(FacialExpression.NEUTRAL, "Have you brought me the scroll of restoration to complete the ritual?").also { stage++ }
|
||||
2 -> if(inInventory(player, HAZEEL_SCROLL)){
|
||||
playerl(FacialExpression.NEUTRAL, "Yep. Got it right here.").also { stage++ }
|
||||
} else {
|
||||
playerl(FacialExpression.NEUTRAL, "No, not yet.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
3 -> npcl(FacialExpression.NEUTRAL, "FINALLY! Quickly, give it to me and I will begin the ritual!").also { stage++ }
|
||||
4 -> sendDialogue(player, "You hand Alomone the scroll of Hazeel.").also { stage++ }
|
||||
5 -> npcl(FacialExpression.NEUTRAL, "Yes... YES! With the words contained within this scroll our Lord shall return to this realm and bring destruction to our enemies!").also { stage++ }
|
||||
6 -> npcl(FacialExpression.NEUTRAL, "Watch adventurer, and witness the glorious rebirth of Hazeel!").also { stage++ }
|
||||
7 -> npcl(FacialExpression.NEUTRAL, "Lord Hazeel... Lord Zamorak... I call upon the powers of evil... The forces of chaos... The strength of hatred...").also { stage++ }
|
||||
8 -> npcl(FacialExpression.NEUTRAL, "Sentente sillaberi junque dithmento! Ia! Ia! dextrimon encanto! termando... imcando... solly enty rando... sentente! Ia! Ia! Indenti zaggarati g'thxa!").also { stage++ }
|
||||
9 -> sendDialogue(player, "Alomone continues to recite the scroll. It is in a language you have never heard and do not understand at all.").also { stage++ }
|
||||
10 -> npcl(FacialExpression.NEUTRAL, "Dintenta! Sententa! Retenta! Q'exjta! Ia! Sottottott! Ia! Dysmenta junque fammatio svelken! Sottey! Sentey! SOLOMENT!").also { stage++ }
|
||||
11 -> {
|
||||
sendDialogue(player, "As Alomone finishes reading the scroll you hear a shrill scream emanate from Hazeel's coffin.").also { stage++ }
|
||||
hazeel.init()
|
||||
setAttribute(player,"hazeelSpawned", true)
|
||||
player.face(hazeel)
|
||||
hazeel.isWalks = false
|
||||
}
|
||||
12 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "My loyal followers. I have pride in you all. Never did I expect to return to this land and I see I have much to attend to.").also { stage++ }
|
||||
13 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Soon this world will cower once more at the name Hazeel, and my fury will blaze across mine enemies just as my loyal followers gain their rewards.").also { stage++ }
|
||||
14 -> sendDialogue(player, "Hazeel turns to look at you directly.").also{ hazeel.face(player) }.also { stage++ }
|
||||
15 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Adventurer. I know that thy efforts were principal in my return to this plain of existence. I offer you my thanks directly.").also { stage++ }
|
||||
16 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "You may not be a follower of Zamorak directly but your cunning and treachery mark you as a friend to the Mahjarrat. Rest assured; I will call on your assistance again.").also { stage++ }
|
||||
17 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Weak as I am now, my strength shall return, and my followers will be greatly rewarded when my powers peak once more. Join me in my cause as my servant, and all").also { stage++ }
|
||||
18 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "the riches of this plane shall be yours to share, in domination over all who oppose my kind. You would be wise to pledge yourself now while you still may.").also { stage++ }
|
||||
19 -> playerl(FacialExpression.ANGRY, "I serve nobody but myself.").also { stage++ }
|
||||
20 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Your insolence shall be overlooked this time for I am in truth in your debt. Come close: I shall reward you with wealth such as you deserve.").also { stage++ }
|
||||
21 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Although your true reward will be later, when Zamorak and I, together, lay waste to this miserable planet, and you are spared as an ally to us.").also { stage++ }
|
||||
22 -> sendDialogue(player, "Hazeel gives you some coins. They seem to be extremely ancient.").also { stage++ }
|
||||
23 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "And now I must leave you my loyal subjects for time is short, and I have many pressing matters to address before I make my return. I must leave now, and head Northwards").also { stage++ }
|
||||
24 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "to join my fellow mahjarrat and regain my strength and powers. I shall return when I am ready to fight my enemies and much blood shall flow across these lands!").also { stage++ }
|
||||
25 -> sendDialogue(player, "The cultists let out a mighty cheer.").also { stage++ }
|
||||
26 -> {
|
||||
end()
|
||||
removeItem(player, HAZEEL_SCROLL)
|
||||
val hazeel = player.findNpcInViewport(NPCs.HAZEEL_892)
|
||||
hazeel?.clear()
|
||||
removeAttribute(player,"hazeelSpawned")
|
||||
finishQuest(player, Quests.HAZEEL_CULT)
|
||||
}
|
||||
}
|
||||
|
||||
// stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
// also valid for stages 4 and 5 in the carnillean arc.
|
||||
(questStage in 4..100) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hi there.").also { stage++ }
|
||||
} else {
|
||||
npcl(FacialExpression.ANNOYED, "You have crossed my path too many times intruder. Leave or face my wrath.").also { stage = 2 }
|
||||
}
|
||||
}
|
||||
1 -> npcl(FacialExpression.NEUTRAL, "Welcome, adventurer. Know that as a friend to Hazeel, you are always welcome here.").also { stage = END_DIALOGUE }
|
||||
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "Yeah, whatever.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// function used to find any hazeels and remove them at the end of the quest.
|
||||
// this has the potential to remove other players' hazeels, but the player could still complete the quest by either continuing their
|
||||
// active dialogue or speaking to alomone, and just dealing with the fact that their hazeel is not on the map. relogging and restarting
|
||||
// the dialogue would get these players a new hazeel.
|
||||
private fun Player.findNpcInViewport(npcId: Int): NPC? {
|
||||
val viewport = viewport ?: return null
|
||||
for (plane in viewport.viewingPlanes) {
|
||||
for (npc in plane.npcs) {
|
||||
if (npc.id == npcId) {
|
||||
return npc
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Hazeel is in this array because in the event the player quits the dialogue halfway through in the final stage, it can be restarted by speaking to either alomone or hazeel.
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.ALOMONE_891, NPCs.HAZEEL_892)
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.carnilleanArc
|
||||
import core.api.isQuestInProgress
|
||||
import core.api.produceGroundItem
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.npc.AbstractNPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.map.Location
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.*
|
||||
|
||||
/**
|
||||
* Alomone drops the Carnillean Amour during the Hazeel Cult quest.
|
||||
*/
|
||||
|
||||
@Initializable
|
||||
class AlomoneNPC(
|
||||
id: Int = 0,
|
||||
location: Location? = null,
|
||||
) : AbstractNPC(id, location) {
|
||||
override fun construct(
|
||||
id: Int,
|
||||
location: Location,
|
||||
vararg objects: Any,
|
||||
): AbstractNPC = AlomoneNPC(id, location)
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.ALOMONE_891)
|
||||
|
||||
override fun finalizeDeath(killer: Entity?) {
|
||||
if (killer is Player) {
|
||||
if (isQuestInProgress(killer, Quests.HAZEEL_CULT, 3, 4) && carnilleanArc(killer)) {
|
||||
produceGroundItem(killer, Items.CARNILLEAN_ARMOUR_2405, 1, this.location)
|
||||
}
|
||||
super.finalizeDeath(killer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import core.api.getQuestStage
|
||||
import core.api.inEquipmentOrInventory
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class ButlerJonesDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int): Boolean {
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
|
||||
when {
|
||||
// stage 3 - poison poured in food (mahjarrat-only stage)
|
||||
(questStage == 3) -> when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.NEUTRAL, "Hello, adventurer. Such a terrible shame about Scruffy. I wonder if the family will ever fully recover.").also { stage++ }
|
||||
2 -> npcl(FacialExpression.FRIENDLY, "Anyway, I hear your quest is going well.").also { stage++ }
|
||||
3 -> playerl(FacialExpression.FRIENDLY, "Really?").also { stage++ }
|
||||
4 -> npcl(FacialExpression.ANNOYED, "Oh yes. Do keep up the good work.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 4 - alomone either fought or he tells you he needs scroll
|
||||
(questStage == 4 && mahjarratArc(player) && inEquipmentOrInventory(player, Items.HAZEELS_MARK_2406)) -> when (stage) {
|
||||
0 -> npcl(FacialExpression.NEUTRAL, "Hello again friend. I see you have the mark now; you should keep it covered up.").also { stage++ }
|
||||
1 -> playerl(FacialExpression.FRIENDLY, "That? Oh, it's nothing. Just an old amulet.").also { stage++ }
|
||||
2 -> npcl(FacialExpression.NEUTRAL, "You don't have to pretend with me, friend! Our cause is one and the same; the sooner Lord Hazeel is avenged the better for this city... and for us!").also { stage++ }
|
||||
3 -> playerl(FacialExpression.FRIENDLY, "So do you have any idea where the scroll with the enchantment is?").also { stage++ }
|
||||
4 -> npcl(FacialExpression.ANNOYED, "No idea I'm afraid. I KNOW it's somewhere in this house, but for the life of me I can't find it anywhere. I've searched high and low for it!").also { stage++ }
|
||||
5 -> playerl(FacialExpression.FRIENDLY, "And Sir Ceril doesn't suspect a thing?").also { stage++ }
|
||||
6 -> npcl(FacialExpression.ANNOYED, "Ha! That silly old fool? He can't see the forest for the trees.").also { stage++ }
|
||||
7 -> playerl(FacialExpression.FRIENDLY, "I'll keep on looking then...").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 0 - unstarted
|
||||
// stage 1 - after talking to ceril
|
||||
// stage 2 - talk to clivet (set attr for carnillean)
|
||||
// stage 2 - talk to clivet (set attr for mahjarrat)
|
||||
// stage 5 - either returning the armour, or finding the scroll
|
||||
// also stage 4 if you are carnillean side
|
||||
(questStage in 0..5) -> when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.FRIENDLY, "Hello. How are you today?").also { stage++ }
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "Good thank you, and yourself?").also { stage++ }
|
||||
3 -> npcl(FacialExpression.FRIENDLY, "Very well, thank you.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
(questStage == 100) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello stranger.").also { stage = 1 }
|
||||
} else {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 6 }
|
||||
}
|
||||
}
|
||||
1 -> npcl(FacialExpression.NEUTRAL, "It's an honour to be in your presence again, adventurer. I hope things are well?").also { stage++ }
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "Not bad, thanks. Yourself?").also { stage++ }
|
||||
3 -> npcl(FacialExpression.ANNOYED, "Unfortunately, I am still forced to deal with this insufferable family. Many generations have passed, but they are still the enemy. As such, they must be kept a close eye on.").also { stage++ }
|
||||
4 -> npcl(FacialExpression.ANNOYED, "Still, I have no doubt that the time will soon come for me to leave this place. Our lord will certainly have need of me elsewhere once his current work is complete.").also { stage++ }
|
||||
5 -> playerl(FacialExpression.FRIENDLY, "I see. Well good luck with it all.").also { stage = END_DIALOGUE }
|
||||
|
||||
6 -> npcl(FacialExpression.ANNOYED, "Why hello there.").also { stage++ }
|
||||
7 -> playerl(FacialExpression.FRIENDLY, "I take it you're the new butler...?").also { stage++ }
|
||||
8 -> npcl(FacialExpression.ANNOYED, "That's right. I hear that they had some problems with the last one.").also { stage++ }
|
||||
9 -> playerl(FacialExpression.FRIENDLY, "Yes, you could say that...").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.BUTLER_JONES_890)
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.carnilleanArc
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.fakeFinish
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class CerilCarnilleanDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int): Boolean {
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
when {
|
||||
// stage 0 - unstarted
|
||||
(questStage == 0) -> {
|
||||
when (stage) {
|
||||
0 -> npcl(FacialExpression.ANGRY, "Blooming, thieving, weirdo cultists! Why don't they leave me alone? String 'em all up, that's what I say!").also { stage++ }
|
||||
1 -> options("What's wrong?", "You probably deserve it.", "You seem uptight, I'll leave you alone.").also { stage++ }
|
||||
2 -> when (buttonID) {
|
||||
1 -> playerl(FacialExpression.HALF_ASKING, "What's wrong?").also { stage++ }
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "You probably deserve it.").also { stage = 20 }
|
||||
3 -> playerl(FacialExpression.FRIENDLY, "You seem uptight, I'll leave you alone.").also { stage = 21 }
|
||||
}
|
||||
3 -> npcl(FacialExpression.ANGRY, "It's those blooming cultists from the forest! Those freaks keep breaking into my house!").also { stage++ }
|
||||
4 -> playerl(FacialExpression.FRIENDLY, "Have they taken much?").also { stage++ }
|
||||
5 -> npcl(FacialExpression.ANGRY, "They first broke in months ago and stole a suit of armour. The strange thing is that they've broken in four times since but taken nothing.").also { stage++ }
|
||||
6 -> playerl(FacialExpression.FRIENDLY, "And you are...?").also { stage++ }
|
||||
7 -> npcl(FacialExpression.ANGRY, "Why, I am Sir Ceril Carnillean! We really are quite a famous bloodline... who've played a vital role in the politics of Ardougne for many generations.").also { stage++ }
|
||||
8 -> npcl(FacialExpression.FRIENDLY, "Perhaps you would be able to assist me in returning the stolen armour? For a modest cash reward of course!").also { stage++ }
|
||||
9 -> options("Yes, of course, I'd be happy to help.", "No thanks. I've got other plans.").also { stage++ }
|
||||
10 -> when (buttonID) {
|
||||
1 -> playerl(FacialExpression.FRIENDLY, "Yes, of course, I'd be happy to help.").also { stage++ }
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "No thanks. I've got other plans.").also { stage = 19 }
|
||||
}
|
||||
11 -> npcl(FacialExpression.FRIENDLY, "That's very noble of you sirrah! I caught a glimpse of the thieves leaving but due to... uh... yeah, my cold I was unable to give chase.").also { stage++ }
|
||||
12 -> npcl(FacialExpression.FRIENDLY, "They're some kind of crazy cult who dress all in black, and hang out near the cave entrance in the forest south of here.").also { stage++ }
|
||||
13 -> playerl(FacialExpression.FRIENDLY, "How do you know that?").also { stage++ }
|
||||
14 -> npcl(FacialExpression.FRIENDLY, "My old butler, Higson, once followed them to their hideout there. Unfortunately the next night he died in his sleep.").also { stage++ }
|
||||
15 -> playerl(FacialExpression.FRIENDLY, "That's awful!").also { stage++ }
|
||||
16 -> npcl(FacialExpression.FRIENDLY, "No, it's okay. A replacement arrived the next day. He's been great - cooks an excellent broth!").also { stage++ }
|
||||
|
||||
17 -> playerl(FacialExpression.FRIENDLY, "Ok. I'll see what I can do.").also { stage++ }
|
||||
18 -> {
|
||||
end()
|
||||
setQuestStage(player, Quests.HAZEEL_CULT, 1)
|
||||
}
|
||||
19 -> npcl(FacialExpression.FRIENDLY, "Well no wonder I'm the one with the big house and you're the one on the streets.").also { stage = END_DIALOGUE }
|
||||
20 -> npcl(FacialExpression.FRIENDLY, "And who are you to judge me? You look like a peasant anyway. I'm wasting my time talking to you.").also { stage = END_DIALOGUE }
|
||||
21 -> npcl(FacialExpression.FRIENDLY, "Yes, I doubt you could help anyway.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 3 - poison poured in food (mahjarrat-only stage)
|
||||
(questStage == 3) -> when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello again.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.ANNOYED, "Oh the inhumanity... the cruelty... the misery... the pain...").also { stage++ }
|
||||
2 -> npcl(FacialExpression.ANNOYED, "My son is a good boy, really, but how could he give his dinner to Scruffy without having the servants test it for poison first? How?").also { stage++ }
|
||||
3 -> npcl(FacialExpression.ANNOYED, "How could he be so thoughtless and careless? He knows we are all under threat!").also { stage++ }
|
||||
4 -> playerl(FacialExpression.FRIENDLY, "Scruffy?").also { stage++ }
|
||||
5 -> npcl(FacialExpression.ANNOYED, "He's been with our family for twenty years... that's 140 in dog years! The poor dog... What did he ever do to deserve such a fate?").also { stage++ }
|
||||
6 -> playerl(FacialExpression.FRIENDLY, "Your dog got poisoned? That's not right.").also { stage++ }
|
||||
7 -> npcl(FacialExpression.NEUTRAL, "I agree! I hope whichever evildoer is responsible gets the full weight of the law brought upon them!").also { stage++ }
|
||||
8 -> playerl(FacialExpression.NEUTRAL, "Uh... yeah... me too.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 4 - alomone either fought or he tells you he needs scroll
|
||||
(questStage == 4 && carnilleanArc(player) && inInventory(player, Items.CARNILLEAN_ARMOUR_2405)) -> when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Look! I've recovered your armour!").also { stage++ }
|
||||
1 -> npcl(FacialExpression.FRIENDLY, "Well done! I must say I am very impressed! Come on, hand it over.").also { stage++ }
|
||||
2 -> npcl(FacialExpression.FRIENDLY, "Before we send you on your way with your payment, I'll just get Jones to whip you up a batch of his special broth.").also { stage++ }
|
||||
3 -> playerl(FacialExpression.FRIENDLY, "I'd rather not if it's all the same to you. I overheard the cultists talking, and apparently Jones is in league with them.").also { stage++ }
|
||||
4 -> {
|
||||
// check if we are upstairs
|
||||
if(player.location.z == 1) {
|
||||
npcl(FacialExpression.ANGRY, "W-what? RIGHT! You! We're going to blooming well sort this out right now once and for all!").also { stage++ }
|
||||
} else {
|
||||
npcl(FacialExpression.ANGRY, "W-what? RIGHT! You! Come with me upstairs! We're going to blooming-well sort this out, once and for all!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
5 -> npcl(FacialExpression.HALF_ASKING, "Jones! This commoner says you had something to do with the theft of my armour. What do you have to say for yourself about that?").also { stage++ }
|
||||
6 -> sendNPCDialogue(player, NPCs.BUTLER_JONES_890, "It wasn't me m'lud. I am, as you know, a loyal servant.").also { stage++ }
|
||||
7 -> npcl(FacialExpression.FRIENDLY, "Humph. Quite right too. I cannot fathom why this scoundrel would accuse you of such a crime without evidence to back up @g[his,her] accusations.").also { stage++ }
|
||||
8 -> npcl(FacialExpression.FRIENDLY, "Right, I have decided. I have given my word as a nobleman to reward you for your efforts in retrieving my armour.").also { stage++ }
|
||||
// it is authentic that the previous line ends in a period and the next line starts with a non-capital 'but'
|
||||
9 -> npcl(FacialExpression.FRIENDLY, "but I must also compensate Jones for this terrible slander you have made against him.").also { stage++ }
|
||||
10 -> {
|
||||
sendDialogue(player, "Sir Ceril gives you 5 gold. Sir Ceril gives Jones 1995 gold.")
|
||||
addItemOrDrop(player, Items.COINS_995, 5)
|
||||
stage++
|
||||
}
|
||||
11 -> npcl(FacialExpression.ANGRY, "Now take it and leave you scoundrel! And don't darken my doorstep again!").also { stage++ }
|
||||
12 -> sendNPCDialogue(player, NPCs.BUTLER_JONES_890, "Don't worry m'lud, this fool won't be bothering us any more.").also { stage++ }
|
||||
13 -> sendDialogue(player, "Jones smirks at you. You are going to need more than words to prove Jones' treachery.").also { stage++ }
|
||||
14 -> {
|
||||
end()
|
||||
removeItem(player, Items.CARNILLEAN_ARMOUR_2405)
|
||||
setQuestStage(player, Quests.HAZEEL_CULT, 5)
|
||||
fakeFinish(player)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stage 5 - either returning the armour, or finding the scroll
|
||||
(questStage == 5 && carnilleanArc(player)) -> when (stage) {
|
||||
// The ceril-jones-player dialogue is continued in the listener for the wardrobe where the player finds the evidence that Jones is bad.
|
||||
0 -> npcl(FacialExpression.ANGRY, "Leave, you scoundrel! And don't darken my doorstep again!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 1 - after talking to ceril
|
||||
// stage 2 - talk to clivet (set attr for carnillean)
|
||||
// stage 2 - talk to clivet (set attr for mahjarrat)
|
||||
// also covers stage 4 and 5 if you're playing the mahjarrat side
|
||||
(questStage in 1..99) -> when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello, Ceril.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.ANNOYED, "That's Sir Ceril to you, you impudent scamp. Show a bit of respect to your betters. Now, shouldn't you be recovering my armour?").also { stage++ }
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "Yeah yeah, I'm on it.").also { stage++ }
|
||||
3 -> npcl(FacialExpression.ANNOYED, "Good. I suggest you start with that cave south of the city, near the Clock Tower. That is where the cult is based.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
(questStage == 100) -> when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello, Ceril.").also { stage++ }
|
||||
1 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
npcl(FacialExpression.FRIENDLY, "Oh... I may be wrong... but ever since I asked for your help, things around here have gone from bad to worse...").also { stage = 3 }
|
||||
} else {
|
||||
npcl(FacialExpression.FRIENDLY, "Well hello again adventurer! It's good to see you again! If it wasn't for your quick thinking the treacherous Jones would have poisoned my family and me by now! We are in your debt.").also { stage = 2 }
|
||||
}
|
||||
}
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "Don't worry about it, a good deed is its own reward. And that 2000 gold didn't hurt either.").also { stage = END_DIALOGUE }
|
||||
|
||||
3 -> npcl(FacialExpression.NEUTRAL, "I think you'd better keep out of my way. And for the last time, it's Sir Ceril! Sir! It's not that hard to blooming remember!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.CERIL_CARNILLEAN_885)
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class ClausTheChefDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int): Boolean {
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
when {
|
||||
|
||||
// stage 0 - unstarted
|
||||
// stage 1 - after talking to ceril
|
||||
// stage 2 - talk to clivet (set attr for carnillean)
|
||||
// stage 2 - talk to clivet (set attr for mahjarrat)
|
||||
// stage 3 - poison poured in food (mahjarrat-only stage)
|
||||
// stage 4 - alomone either fought or he tells you he needs scroll
|
||||
// stage 5 - either returning the armour, or finding the scroll
|
||||
(questStage in 0..99) -> {
|
||||
when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.FRIENDLY, "Sorry, can't stop to chat! You would be amazed at how many meals this family gets through daily!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
(questStage == 100) -> {
|
||||
when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hey.").also { stage++ }
|
||||
} else {
|
||||
playerl(FacialExpression.FRIENDLY, "Hiya.").also { stage = 2 }
|
||||
}
|
||||
}
|
||||
1 -> npcl(FacialExpression.NEUTRAL, "Oh, hello there. Sorry, but I can't really talk right now. Things haven't been great here recently, and I have a lot of work to do.").also { stage = END_DIALOGUE }
|
||||
|
||||
2 -> npcl(FacialExpression.HALF_ASKING, "Well hello there adventurer! Are we fit and well?").also { stage++ }
|
||||
3 -> playerl(FacialExpression.FRIENDLY, "Yep, fine thanks.").also { stage++ }
|
||||
4 -> npcl(FacialExpression.FRIENDLY, "Glad to hear it.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.CLAUS_THE_CHEF_886)
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrCarnillean
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrHazeel
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class ClivetDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int, ): Boolean {
|
||||
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
|
||||
when {
|
||||
// stage 0 - unstarted
|
||||
(questStage == 0) -> when (stage) {
|
||||
0 -> npcl(FacialExpression.ANNOYED, "What do you want traveller?").also { stage++ }
|
||||
1 -> playerl(FacialExpression.ANNOYED, "Just passing by.").also { stage++ }
|
||||
2 -> npcl(FacialExpression.ANNOYED, "You have no business here.").also { stage++ }
|
||||
3 -> npcl(FacialExpression.ANGRY, "Leave...now").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 1 - after talking to ceril
|
||||
(questStage == 1) -> when (stage) {
|
||||
0 -> playerl(FacialExpression.ANNOYED, "Do you know the Carnilleans?").also { stage++ }
|
||||
1 -> npcl(FacialExpression.ANNOYED, "You mind your business, I'll mind mine.").also { stage++ }
|
||||
2 -> playerl(FacialExpression.ANNOYED, "Look I KNOW you're hiding something. I've heard there's a cult hideout down here.").also { stage++ }
|
||||
3 -> npcl(FacialExpression.ANGRY, "If you want to stay healthy you'll leave now.").also { stage++ }
|
||||
4 -> playerl(FacialExpression.ANNOYED, "I have my orders.").also { stage++ }
|
||||
5 -> npcl(FacialExpression.HALF_ASKING, "So... that two faced cold hearted snob has made you fall for his propaganda eh?").also { stage++ }
|
||||
6 -> playerl(FacialExpression.FRIENDLY, "Sir Ceril Carnillean is a man of honour!").also { stage++ }
|
||||
7 -> npcl(FacialExpression.ANNOYED, "Is he now? Is he REALLY? There is a lot more to the Carnilleans than meets the eye you fool...").also { stage++ }
|
||||
8 -> npcl(FacialExpression.ANNOYED, "And none of it is honourable.").also { stage++ }
|
||||
9 -> options("What do you mean?", "I've heard enough of your rubbish.").also { stage++ }
|
||||
10 -> when (buttonID) {
|
||||
1 -> playerl(FacialExpression.HALF_ASKING, "What do you mean?").also { stage++ }
|
||||
2 -> playerl(FacialExpression.ANNOYED, "I've heard enough of your rubbish.").also { stage = 22 }
|
||||
}
|
||||
11 -> npcl(FacialExpression.ANNOYED, "The Carnillean home does not belong to them. The builder of the house was Lord Hazeel, one of the Mahjarrat followers of Zamorak. Many years ago there was a civil war").also { stage++ }
|
||||
12 -> npcl(FacialExpression.ANNOYED, "in this land, and the hateful Saradominists declared war upon all Zamorakians who lived here. Lord Hazeel nobly would not repent his beliefs, and the Carnilleans harassed").also { stage++ }
|
||||
13 -> npcl(FacialExpression.ANNOYED, "Hazeel and his followers for many decades. One fateful night, under cover of darkness, they stormed his home in an angry mob torturing and butchering all loyal").also { stage++ }
|
||||
14 -> npcl(FacialExpression.ANNOYED, "Zamorakians they encountered inside. The following morning, the Carnillean forefathers moved into the empty household and claimed it as their own.").also { stage++ }
|
||||
15 -> npcl(FacialExpression.ANNOYED, "They have grown fat on the hard work of Lord Hazeel ever since. Unluckily for them Lord Hazeel as a Mahjarrat had access to powers and enchantments they knew nothing").also { stage++ }
|
||||
16 -> npcl(FacialExpression.ANNOYED, "about, and made preparations for his return when first they began to storm his home. Soon the day will come when he will return to wreak his vengeance upon the thieves!").also { stage++ }
|
||||
17 -> playerl(FacialExpression.ANNOYED, "The politics and histories of Ardougne do not concern me. I have been given a job and intend to see it through to the end.").also { stage++ }
|
||||
18 -> npcl(FacialExpression.ANNOYED, "Well then friend, perhaps I can offer you a different job then? Sooner as later Our master WILL return to this land; those faithful to him will be well rewarded.").also { stage++ }
|
||||
19 -> npcl(FacialExpression.ANNOYED, "Join our cult and assist us in his restoration, and you will be well rewarded; try and prevent his return and suffer the wrath of Zamorak and the Mahjarrat.").also { stage++ }
|
||||
20 -> options("You're crazy, I'd never help you.", "So what would I have to do?").also { stage++ }
|
||||
21 -> when (buttonID) {
|
||||
1 -> playerl(FacialExpression.DISGUSTED_HEAD_SHAKE, "You're crazy, I'd never help you.").also { stage++ }
|
||||
2 -> playerl(FacialExpression.HALF_ASKING, "So what would I have to do?").also { stage = 40 }
|
||||
}
|
||||
|
||||
// SET CARNILLEAN ARC
|
||||
22 -> npcl(FacialExpression.ANNOYED, "Then you are a fool. Go back to your small minded mundane little life; you will never know the glories you could have tasted as one of us!").also { stage++ }
|
||||
23 -> {
|
||||
end()
|
||||
setAttribute(player, attrCarnillean, true)
|
||||
setQuestStage(player, Quests.HAZEEL_CULT, 2)
|
||||
runTask(player, 1) {
|
||||
npc.isInvisible = true
|
||||
sendDialogue(player, "The man jumps onto the raft and pushes off down into the sewer system.")
|
||||
sendMessage(player, "Clivet: You'll never find us...")
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
}
|
||||
|
||||
// SET MAHJARRAT ARC
|
||||
40 -> npcl(FacialExpression.NEUTRAL, "You must prove your loyalty to our cause. Killing one of the Carnillean household should be sufficient proof of your dedication to resurrecting our master Lord Hazeel.").also { stage++ }
|
||||
41 -> npcl(FacialExpression.NEUTRAL, "So what say you adventurer? Join our side against the Carnillean thieves?").also { stage++ }
|
||||
42 -> options("No. I won't do it.", "Ok, count me in.").also { stage++ }
|
||||
43 -> when (buttonID) {
|
||||
1 -> playerl(FacialExpression.DISGUSTED_HEAD_SHAKE, "No. I won't do it.").also { stage = 22 }
|
||||
2 -> playerl(FacialExpression.NEUTRAL, "Ok, count me in.").also { stage++ }
|
||||
}
|
||||
44 -> npcl(FacialExpression.NEUTRAL, "Excellent. It takes a rare character to see through the cursed Carnillean lies. I can see you are a person of exactly the right character to join the followers of Hazeel.").also { stage++ }
|
||||
45 -> npcl(FacialExpression.NEUTRAL, "Here, take this poison. Pour it into their food, and once the deed is done, return here and speak to me once more.").also { stage++ }
|
||||
46 -> {
|
||||
end()
|
||||
setAttribute(player, attrHazeel, true)
|
||||
setQuestStage(player, Quests.HAZEEL_CULT, 2)
|
||||
addItemOrDrop(player, Items.POISON_273)
|
||||
}
|
||||
}
|
||||
|
||||
// stage 2 - talk to clivet (set attr for carnillean)
|
||||
// stage 2 - talk to clivet (set attr for mahjarrat)
|
||||
(questStage == 2) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player) && !inInventory(player, Items.POISON_273)) {
|
||||
playerl(FacialExpression.FRIENDLY, "I need some more poison.").also { stage++ }
|
||||
} else if (mahjarratArc(player) && inInventory(player, Items.POISON_273)){
|
||||
npcl(FacialExpression.FRIENDLY, "You have a mission for us, adventurer. Go to the Carnilliean household and poison Ceril Carnillean's meal to prove your loyalty.").also { stage = END_DIALOGUE }
|
||||
} else {
|
||||
playerl(FacialExpression.HALF_ASKING, "WHERE is the cult hideout?").also { stage = 3 }
|
||||
}
|
||||
}
|
||||
1 -> npcl(FacialExpression.ANNOYED, "Fool! Be more careful with it this time.").also { stage++ }
|
||||
2 -> {
|
||||
end()
|
||||
addItemOrDrop(player, Items.POISON_273)
|
||||
}
|
||||
3 -> npcl(FacialExpression.ANNOYED, "You're more of a fool than you look if you think you will ever find it. When Lord Hazeel is revived you will be the first to grovel for his mercy!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 3 - poison poured in food (mahjarrat-only stage)
|
||||
(questStage == 3 && mahjarratArc(player)) -> when (stage) {
|
||||
0 -> {
|
||||
if (!inEquipmentOrInventory(player, Items.HAZEELS_MARK_2406)) {
|
||||
playerl(FacialExpression.FRIENDLY, "I poured the poison into the Carnillean's meal as requested. It didn't quite go to plan however.").also { stage++ }
|
||||
} else {
|
||||
playerl(FacialExpression.HALF_ASKING, "What was I supposed to do again?").also { stage = 6 }
|
||||
}
|
||||
}
|
||||
1 -> npcl(FacialExpression.NEUTRAL, "Yes, we heard all about it from one of our sources. Hazeel has eyes everywhere! You have now proved your loyalty and will be rewarded when Hazeel is revived!").also { stage++ }
|
||||
2 -> playerl(FacialExpression.HALF_ASKING, "Ok. So what's next?").also { stage++ }
|
||||
3 -> npcl(FacialExpression.NEUTRAL, "Here. Wear this amulet; it is called The Sign of Hazeel. Not only will it identify you as one of our brotherhood to other followers but it is also the key to finding our hideout.").also { addItemOrDrop(player, Items.HAZEELS_MARK_2406) }.also { stage++ }
|
||||
4 -> playerl(FacialExpression.NEUTRAL, "How does this amulet help do that then?").also { stage++ }
|
||||
5 -> npcl(FacialExpression.NEUTRAL, "Hazeel in his wisdom when he built this lair did so by making it inaccessible unless the flow of the sewers is controlled exactly by the sewer valves above ground.").also { stage++ }
|
||||
6 -> npcl(FacialExpression.NEUTRAL, "Each sewer valve must be correctly set before the water level is such that you may enter. The secret of the correct settings is contained in these amulets which honour him.").also { stage++ }
|
||||
7 -> npcl(FacialExpression.NEUTRAL, "Starting from left to right, follow the design of the amulet to locate each sewer valve, and then turn each so as to follow the amulet design.").also { stage++ }
|
||||
8 -> npcl(FacialExpression.NEUTRAL, "When you solve the sequence you may enter our hideout where our leader, Alomone, awaits you.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
// stage 4 - alomone either fought or he tells you he needs scroll
|
||||
// stage 5 - either returning the armour, or finding the scroll
|
||||
// stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
(questStage in 4..100) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ }
|
||||
} else {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 4 }
|
||||
}
|
||||
}
|
||||
1 -> npcl(FacialExpression.FRIENDLY, "It is good to see you once more, adventurer. Glory to Hazeel!").also { stage++ }
|
||||
2 -> {
|
||||
// if player doesn't have hazeel's mark they can get a new copy.
|
||||
if (hasAnItem(player, Items.HAZEELS_MARK_2406).container == null) {
|
||||
playerl(FacialExpression.FRIENDLY, "I've lost my Mark of Hazeel.").also { stage++ }
|
||||
} else {
|
||||
playerl(FacialExpression.FRIENDLY, "Glory to Hazeel!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
3 -> npcl(FacialExpression.ANNOYED, "Here, take this.").also { addItemOrDrop(player, Items.HAZEELS_MARK_2406) }.also { stage = END_DIALOGUE }
|
||||
|
||||
4 -> npcl(FacialExpression.ANNOYED, "You may have won this battle meddler, but the war rages on. Go bother some goblins or something, for when Hazeel returns your destruction is assured.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.CLIVET_893)
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import core.api.*
|
||||
import core.game.node.entity.npc.AbstractNPC
|
||||
import core.game.world.map.Location
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.*
|
||||
|
||||
/**
|
||||
* Clivet disappears briefly during the Hazeel Cult quest.
|
||||
*/
|
||||
|
||||
@Initializable
|
||||
class ClivetNPC(id: Int = 0, location: Location? = null, ) : AbstractNPC(id, location) {
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any, ): AbstractNPC = ClivetNPC(id, location)
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.CLIVET_893)
|
||||
|
||||
private var invisibilityTimerRunning = false
|
||||
|
||||
override fun tick() {
|
||||
|
||||
if (isInvisible && !invisibilityTimerRunning) {
|
||||
invisibilityTimerRunning = true
|
||||
|
||||
runTask(this, 20) {
|
||||
isInvisible = false
|
||||
invisibilityTimerRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
super.tick()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import core.api.getQuestStage
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class GuardHCDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int): Boolean {
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
|
||||
when {
|
||||
// stage 0 - unstarted
|
||||
(questStage == 0) -> {
|
||||
when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.FRIENDLY, "Hello. If you've come to admire the Carnillean family home, I must warn you not to cause any trouble.").also { stage++ }
|
||||
2 -> npcl(FacialExpression.FRIENDLY, "Due to recent criminal activities against such a prominent member of Ardougnian politics, I have been sent here on special guard duty to ensure the Carnilleans' security.").also { stage++ }
|
||||
3 -> npcl(FacialExpression.FRIENDLY, "Anyone caught interfering with the person or possessions of said Carnilleans will be taken to jail so quickly that their feet won't touch the floor!").also { stage++ }
|
||||
4 -> playerl(FacialExpression.FRIENDLY, "Well, I wasn't actually planning on doing anything like that...").also { stage++ }
|
||||
5 -> npcl(FacialExpression.FRIENDLY, "Glad to hear it. Let's keep it that way, hmmm?").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 3 - poison poured in food (mahjarrat-only stage)
|
||||
(questStage == 3) -> {
|
||||
when (stage) {
|
||||
0 -> npcl(FacialExpression.SAD, "Today is a dark day. Those cultists have been back, and this time they've gone further than ever before! Murder! We can't afford to keep letting them get away with this.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 1 - after talking to ceril
|
||||
// stage 2 - talk to clivet (set attr for carnillean)
|
||||
// stage 2 - talk to clivet (set attr for mahjarrat)
|
||||
// stage 4 - alomone either fought or he tells you he needs scroll
|
||||
// stage 5 - either returning the armour, or finding the scroll
|
||||
(questStage in 1..99) -> {
|
||||
when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.FRIENDLY, "Hello there. I hear you're after that cult who broke in the other night. It always gladdens me when civilians assist the law like this.").also { stage++ }
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "I'm just happy to be of help.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
(questStage == 100) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 1 }
|
||||
} else {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 6 }
|
||||
}
|
||||
}
|
||||
1 -> npcl(FacialExpression.NEUTRAL, "Hello, adventurer. It's a shame you never found that cult. We have a horrible suspicion that there's been another burglary.").also { stage++ }
|
||||
2 -> playerl(FacialExpression.NEUTRAL, "That's worrying.").also { stage++ }
|
||||
3 -> npcl(FacialExpression.NEUTRAL, "Yes, it is. The thing is, we can't even work out what they've taken! It's all very odd.").also { stage++ }
|
||||
4 -> playerl(FacialExpression.HALF_ASKING, "Is there anything more I can do to help?").also { stage++ }
|
||||
5 -> npcl(FacialExpression.NEUTRAL, "I don't think so. Sir Ceril says you've done enough.").also { stage = END_DIALOGUE }
|
||||
|
||||
6 -> npcl(FacialExpression.ANNOYED, "Well well well... if it isn't our very own local hero. Good to see you managed to clear your name. I always had faith in you!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.GUARD_887)
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.HAZEEL_CULT_VARP
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrCarnillean
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrHazeel
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer1R
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer2R
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer3L
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer4R
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer5R
|
||||
import core.api.*
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.quest.Quest
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.Items
|
||||
|
||||
/* HAZEEL CULT
|
||||
*
|
||||
* Primary video source: https://www.youtube.com/watch?v=hFBJx_xERQ4 (carnillean playthrough)
|
||||
* Primary video source: https://www.youtube.com/watch?v=ZYPB823IyRk (mahjarrat playthrough)
|
||||
* https://web.archive.org/web/20081217064620/http://runescape.salmoneus.net/quests/HazeelCult.html
|
||||
* https://web.archive.org/web/20090121182721/http://runehq.com/guide.php?type=quest&id=00297
|
||||
* https://runescape.wiki/w/Hazeel_Cult?oldid=815270
|
||||
*
|
||||
* Varp 223 - Hazeel Cult progress
|
||||
* https://chisel.weirdgloop.org/varbs/display?varplayer=223
|
||||
* Values Stage Description
|
||||
* 0 0 unstarted
|
||||
* 2 1 after talking to ceril
|
||||
* 3 2 talk to clivet (set attr for carnillean)
|
||||
* 4 2 talk to clivet (set attr for mahjarrat)
|
||||
* 5 3 poison poured in food (mahjarrat-only stage)
|
||||
* 6 4 alomone either fought or he tells you he needs scroll
|
||||
* 7 5 either returning the armour, or finding the scroll
|
||||
* 9 100 quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
*
|
||||
* The varp values just appear to track quest stages, so I don't update the varp at intermediate quest stages.
|
||||
*
|
||||
* Good source of half the quest log: https://youtu.be/Kf95y-YdviA?si=kWeA-8oa-22TttE4&t=140
|
||||
*
|
||||
* Original framework of the quest and about half the dialogue is credit to szumaster.
|
||||
*
|
||||
*/
|
||||
|
||||
@Initializable
|
||||
class HazeelCult : Quest(Quests.HAZEEL_CULT, 74, 73, 1, HAZEEL_CULT_VARP, 0, 1, 9) {
|
||||
|
||||
// These functions are used at various points to check whether the player is following the Carnillean's side, or Hazeel's.
|
||||
companion object {
|
||||
fun carnilleanArc(player: Player) : Boolean {
|
||||
return (!getAttribute(player, attrHazeel, false) && getAttribute(player, attrCarnillean, false))
|
||||
}
|
||||
|
||||
fun mahjarratArc(player: Player) : Boolean {
|
||||
return (getAttribute(player, attrHazeel, false) && !getAttribute(player, attrCarnillean, false))
|
||||
}
|
||||
|
||||
// the fake ending where you don't prove Jones guilty yet. lol.
|
||||
fun fakeFinish(player: Player) {
|
||||
openInterface(player, 277)
|
||||
|
||||
player.packetDispatch.sendString("" + player.getQuestRepository().getPoints() + "", 277, 7)
|
||||
player.packetDispatch.sendString("You have... kind of... completed the Hazeel Cult Quest!", 277, 4)
|
||||
|
||||
player.packetDispatch.sendItemZoomOnInterface(Items.COINS_995, 230, 277, 5)
|
||||
|
||||
player.packetDispatch.sendString("5 coins", 277, 10)
|
||||
player.packetDispatch.sendString("", 277, 11)
|
||||
player.packetDispatch.sendString("", 277, 12)
|
||||
player.packetDispatch.sendString("", 277, 13)
|
||||
player.packetDispatch.sendString("", 277, 14)
|
||||
player.packetDispatch.sendString("", 277, 15)
|
||||
player.packetDispatch.sendString("", 277, 16)
|
||||
player.packetDispatch.sendString("", 277, 17)
|
||||
}
|
||||
}
|
||||
|
||||
override fun drawJournal(player: Player, stage: Int) {
|
||||
super.drawJournal(player, stage)
|
||||
var line = 11
|
||||
|
||||
val attributeMahjarrat = mahjarratArc(player)
|
||||
val attributeCarnillean = carnilleanArc(player)
|
||||
|
||||
if (stage >= 0) {
|
||||
line(player, "I can start the quest by talking to !!Sir Ceril Carnillean?? at", line++, stage >= 1)
|
||||
line(player, "the house due !!West?? of !!Ardougne Zoo??.", line++, stage >= 1)
|
||||
line++
|
||||
}
|
||||
if (stage >= 1) {
|
||||
line(player, "I spoke to !!Sir Ceril Carnillean?? at his house and agreed to", line++, stage >= 2)
|
||||
line(player, "help him !!investigate?? the theft of a !!family heirloom??.", line++, stage >= 2)
|
||||
line++
|
||||
}
|
||||
if (stage >= 2) {
|
||||
line(player, "I found a !!member of the cult?? called !!Clivet?? at the entrance", line++, stage >= 3)
|
||||
line(player, "to the !!cult's hideout??, south of !!Ardougne??.", line++, stage >= 3)
|
||||
line++
|
||||
}
|
||||
|
||||
/* --------------- *
|
||||
* HAZEEL SIDE
|
||||
* --------------- */
|
||||
if (stage >= 3 && attributeMahjarrat && !attributeCarnillean) {
|
||||
line(player, "Having decided to assist the cult in their mission to revive !!Hazeel??,", line++, stage >= 4)
|
||||
line(player, "I tried to !!poison?? the !!Carnilleans?? but instead", line++, stage >= 4)
|
||||
line(player, "only managed to kill their pet dog.", line++, stage >= 4)
|
||||
line++
|
||||
}
|
||||
if (stage >= 4 && attributeMahjarrat && !attributeCarnillean) {
|
||||
line(player, "I spoke to !!Clivet?? and he told me that I had failed,", line++, stage >= 5)
|
||||
line(player, "but he gave me an !!amulet?? anyway.", line++, stage >= 5)
|
||||
line(player, "Then I managed to take the sewer rafts to the cult hideout.", line++, stage >= 5)
|
||||
line++
|
||||
}
|
||||
if (stage >= 5 && attributeMahjarrat && !attributeCarnillean) {
|
||||
line(player, "I found the scroll needed to revive !!Hazeel?? in a", line++, stage >= 100)
|
||||
line(player, "secret compartment upstairs at the !!Carnillean Mansion??.", line++, stage >= 100)
|
||||
line++
|
||||
}
|
||||
if (stage == 100 && attributeMahjarrat && !attributeCarnillean) {
|
||||
line(player, "I gave !!Alomone?? the scroll from the !!Carnillean Mansion?? and", line++, true)
|
||||
line(player, "helped perform the revivification of !!Hazeel??.", line++, true)
|
||||
line(player, "I was rewarded for all of my help", line++, true)
|
||||
line(player, "in returning !!Hazeel?? to his followers.", line++, true)
|
||||
line++
|
||||
}
|
||||
|
||||
/* --------------- *
|
||||
* CARNILLEAN SIDE
|
||||
* --------------- */
|
||||
if (stage >= 2 && attributeCarnillean && !attributeMahjarrat) {
|
||||
line(player, "He told me a pack of lies about the !!Carnilleans??,", line++, stage >= 4)
|
||||
line(player, "then asked me to join the !!cult??. Obviously, I refused.", line++, stage >= 4)
|
||||
line(player, "I was still no closer to recovering the !!missing armour??.", line++, stage >= 4)
|
||||
line(player, "After speaking to him, he jumped onto a !!raft?? and headed", line++, stage >= 4)
|
||||
line(player, "into the !!sewer system??. I need to find a way to follow", line++, stage >= 4)
|
||||
line++
|
||||
}
|
||||
if (stage >= 4 && attributeCarnillean && !attributeMahjarrat) {
|
||||
line(player, "I managed to enter !!the hideout??, kill the !!cult leader?? and", line++, stage >= 5)
|
||||
line(player, "retrieve !!the armour??. I discovered that !!Jones the Butler??", line++, stage >= 5)
|
||||
line(player, "was secretly a !!member of the cult?? and a !!traitor??.", line++, stage >= 5)
|
||||
line++
|
||||
}
|
||||
// this stage displays a 'fake' quest ending!
|
||||
// https://youtu.be/hFBJx_xERQ4?si=ft_1rZGKAHpDjind&t=648
|
||||
if (stage >= 5 && attributeCarnillean && !attributeMahjarrat) {
|
||||
line(player, "I returned !!the armour??, but !!Ceril?? didn't believe !!Jones was involved??", line++, stage >= 100)
|
||||
line(player, "with the cult and was responsible for the theft.", line++, stage >= 100)
|
||||
line++
|
||||
}
|
||||
if (stage == 100 && attributeCarnillean && !attributeMahjarrat) {
|
||||
line(player, "I found undeniable evidence that the !!Butler?? was", line++, true)
|
||||
line(player, "involved with the cult and gave it to !!Ceril??.", line++, true)
|
||||
line(player, "My name was cleared and", line++, true)
|
||||
line(player, "I graciously accepted the reward for all of my help.", line++, true)
|
||||
line++
|
||||
}
|
||||
|
||||
/* --------------- *
|
||||
* QUEST COMPLETE
|
||||
* --------------- */
|
||||
if (stage == 100) {
|
||||
line(player, "<col=FF0000>QUEST COMPLETE!</col>", line, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun finish(player: Player) {
|
||||
var ln = 10
|
||||
super.finish(player)
|
||||
player.packetDispatch.sendItemZoomOnInterface(Items.COINS_995, 230, 277, 5)
|
||||
|
||||
drawReward(player, "1 Quest Point", ln++)
|
||||
drawReward(player, "2,000 Coins", ln++)
|
||||
drawReward(player, "1,500 Thieving XP", ln)
|
||||
|
||||
rewardXP(player, Skills.THIEVING, 1500.0)
|
||||
addItemOrDrop(player, Items.COINS_995, 2000)
|
||||
|
||||
// no attributes need removing on quest completion. the sewer valves are not saved, and which quest arc you chose affects the quest journal and post-quest dialogue.
|
||||
}
|
||||
|
||||
// use ::setqueststage 74 0 to reset quest.
|
||||
override fun reset(player: Player) {
|
||||
removeAttribute(player, attrSewer1R)
|
||||
removeAttribute(player, attrSewer2R)
|
||||
removeAttribute(player, attrSewer3L)
|
||||
removeAttribute(player, attrSewer4R)
|
||||
removeAttribute(player, attrSewer5R)
|
||||
removeAttribute(player, attrHazeel)
|
||||
removeAttribute(player, attrCarnillean)
|
||||
}
|
||||
|
||||
override fun newInstance(`object`: Any?): Quest = this
|
||||
}
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.carnilleanArc
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import core.api.*
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.global.action.DoorActionHandler
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.world.map.Location
|
||||
import org.rs09.consts.*
|
||||
|
||||
class HazeelCultListeners : InteractionListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
on(CLIVET_STAIRS, IntType.SCENERY, "climb-up") { player, _ ->
|
||||
teleport(player, location(2586, 3237, 0))
|
||||
sendMessage(player, "You climb up the stairs.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(CAVE_ENTRANCE, IntType.SCENERY, "enter") { player, _ ->
|
||||
teleport(player, location(2570, 9682, 0))
|
||||
sendMessage(player, "You enter the cave.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(CLIVET_RAFT, IntType.SCENERY, "board") { player, node ->
|
||||
// to use the raft, you must also be at either
|
||||
// Carnillean Arc Stage 2 or greater,
|
||||
// Mahjarrat Arc Stage 3 with Hazeel's Mark,
|
||||
// Mahjarrat Arc Stage 4 or greater
|
||||
val questStage = getQuestStage(player, Quests.HAZEEL_CULT)
|
||||
val questReq = if(
|
||||
(carnilleanArc(player) && questStage >= 2) ||
|
||||
(mahjarratArc(player) && inEquipmentOrInventory(player, Items.HAZEELS_MARK_2406) && questStage >= 3) ||
|
||||
(mahjarratArc(player) && questStage >= 4)
|
||||
) {true} else false
|
||||
val raftUnlock = if(
|
||||
getAttribute(player, attrSewer1R, false) &&
|
||||
getAttribute(player, attrSewer2R, false) &&
|
||||
getAttribute(player, attrSewer3L, false) &&
|
||||
getAttribute(player, attrSewer4R, false) &&
|
||||
getAttribute(player, attrSewer5R, false) &&
|
||||
questReq
|
||||
) {true} else false
|
||||
|
||||
|
||||
// check which raft this is.
|
||||
val x = node.location.x
|
||||
|
||||
if (x == 2567) { // raft to cultists. only use if you've turned the valves.
|
||||
if (!raftUnlock) {
|
||||
sendNPCDialogue(player, CLIVET_NPC, "Hey! I don't remember saying you could use that raft!", FacialExpression.ANNOYED)
|
||||
} else {
|
||||
teleport(player, location(2606, 9692, 0))
|
||||
sendDialogue(player, "The raft washes up the sewer, past the islands until it reaches the end of the sewer passage.")
|
||||
}
|
||||
} else { // raft away from cultists. no check on this one since the valve attributes aren't saved.
|
||||
teleport(player, location(2567, 9680, 0))
|
||||
sendDialogue(player, "The raft flows back to the cave entrance.")
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
/*
|
||||
* Valve 1 - This is right across the bridge south of the Carnilleans' house. Turn it right.
|
||||
* Valve 2 - This is right behind Carnilleans' house. Turn it right.
|
||||
* Valve 3 - This is north of the cave. Turn it left.
|
||||
* Valve 4 - This is south of the zoo's penguin cage. Turn it right.
|
||||
* Valve 5 - Go southeast of the fourth valve, past the monk. Turn it right.
|
||||
*
|
||||
* All 4 of the below sources turn the valves in different orders. Text quest guides make no mention of needing a specific order,
|
||||
* even though Clivet's bad-side dialogue makes it seem like it. For this implementation, the player needs to set each valve in the
|
||||
* correct direction, order of the valves does not matter.
|
||||
*
|
||||
* Bad side - turns in order: https://www.youtube.com/watch?v=ZYPB823IyRk
|
||||
* Good side - turns out of order: https://www.youtube.com/watch?v=OuEvnA6Cyrw
|
||||
* Good side - turns out of order: https://www.youtube.com/watch?v=JG7E3uVx4rs
|
||||
* Bad side - turns out of order: https://www.youtube.com/watch?v=T8JyyhDEVSg
|
||||
*
|
||||
* I allow the valve attributes to be set even if quest is unstarted, but getting on the raft checks the quest stage.
|
||||
*/
|
||||
|
||||
// valves 1, 2, 4, 5 should be turned right
|
||||
on(SEWERS, IntType.SCENERY, "turn-right") { player, node ->
|
||||
if (!player.location.withinDistance(node.asScenery().location, 2)) return@on true
|
||||
|
||||
val currentSewer = node.id
|
||||
when (currentSewer) {
|
||||
SEWER_1 -> setAttribute(player, attrSewer1R, true)
|
||||
SEWER_2 -> setAttribute(player, attrSewer2R, true)
|
||||
SEWER_3 -> removeAttribute(player, attrSewer3L)
|
||||
SEWER_4 -> setAttribute(player, attrSewer4R, true)
|
||||
SEWER_5 -> setAttribute(player, attrSewer5R, true)
|
||||
}
|
||||
|
||||
lock(player, 4)
|
||||
animate(player, TURN_VALVE_ANIMATION)
|
||||
sendDialogueLines(player, "You turn the large metal valve to the right. Beneath your feet you", "can hear the sudden sound of rushing water from the sewer.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
// valve 3 should be turned left
|
||||
on(SEWERS, IntType.SCENERY, "turn-left") { player, node ->
|
||||
if (!player.location.withinDistance(node.asScenery().location, 2)) return@on true
|
||||
|
||||
val currentSewer = node.id
|
||||
when (currentSewer) {
|
||||
SEWER_1 -> removeAttribute(player, attrSewer1R)
|
||||
SEWER_2 -> removeAttribute(player, attrSewer2R)
|
||||
SEWER_3 -> setAttribute(player, attrSewer3L, true)
|
||||
SEWER_4 -> removeAttribute(player, attrSewer4R)
|
||||
SEWER_5 -> removeAttribute(player, attrSewer5R)
|
||||
}
|
||||
|
||||
lock(player, 4)
|
||||
animate(player, TURN_VALVE_ANIMATION)
|
||||
sendDialogueLines(player, "You turn the large metal valve to the left. Beneath your feet you", "can hear the sudden sound of rushing water from the sewer.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
// this wardrobe contains some poison and an amulet, used to convince ceril that jones is a baddie
|
||||
on(WARDROBE, IntType.SCENERY, "search") { player, _ ->
|
||||
openDialogue(player, WardrobeRevealDialogueFile())
|
||||
return@on true
|
||||
}
|
||||
|
||||
// siding with Hazeel and trying to poison the Carnilleans
|
||||
onUseWith(IntType.SCENERY, POISON, COOKING_RANGE) { player, _, _ ->
|
||||
if (mahjarratArc(player) && removeItem(player, POISON)) {
|
||||
sendDialogueLines(player, "You pour the poison into the hot pot.", "The poison dissolves into the soup.")
|
||||
setQuestStage(player, Quests.HAZEEL_CULT, 3)
|
||||
} else {
|
||||
sendNPCDialogue(player, NPCs.CLAUS_THE_CHEF_886, "Oi - I don't want people messing around with my range!")
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// crate in the basement. they key can only be obtained in the mahjarrat arc, which locks the secret passage, chest, and hazeel scroll to that arc as well.
|
||||
on(CRATE, IntType.SCENERY, "search") { player, _ ->
|
||||
if (getQuestStage(player, Quests.HAZEEL_CULT) >= 1 &&
|
||||
mahjarratArc(player) &&
|
||||
!inInventory(player, CHEST_KEY)
|
||||
) {
|
||||
sendDialogue(player, "You search the crate. Under various food packages you find an old rusty key.")
|
||||
addItemOrDrop(player, CHEST_KEY, 1)
|
||||
} else {
|
||||
sendMessage(player, "You search the crate but find nothing.")
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// secret wall on the way up to retrieve the hazeel scroll
|
||||
on(SECRET_PASSAGE, IntType.SCENERY, "Knock-at") { player, node ->
|
||||
if (inInventory(player, CHEST_KEY) && player.location.y == 3274) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, node.asScenery())
|
||||
sendMessage(player, "There is something odd about the wall here.")
|
||||
sendMessage(player, "You find a secret passageway")
|
||||
} else if (player.location.y == 3275) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, node.asScenery())
|
||||
} else {
|
||||
sendMessage(player, "Nothing interesting happens.")
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// chest with hazeel scroll. unlocks with key, opens, and contains the scroll.
|
||||
on(Scenery.CHEST_2856, IntType.SCENERY, "Open") { player, _ ->
|
||||
sendMessage(player, "This chest is locked shut.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
// chest with hazeel scroll. unlocks with key, opens, and contains the scroll.
|
||||
onUseWith(IntType.SCENERY, CHEST_KEY, Scenery.CHEST_2856) { player, _, _ ->
|
||||
sendMessage(player, "You unlock the chest.")
|
||||
animate(player, Animations.HUMAN_OPEN_CHEST_536)
|
||||
addScenery(Scenery.CHEST_2857,CHEST_LOCATION, 2, 10)
|
||||
removeScenery(core.game.node.scenery.Scenery(Scenery.CHEST_2856, Location(2565, 3272, 2)))
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// chest with hazeel scroll. unlocks with key, opens, and contains the scroll.
|
||||
on(Scenery.CHEST_2857, IntType.SCENERY, "Close") { _, _ ->
|
||||
addScenery(Scenery.CHEST_2856, CHEST_LOCATION, 2, 10)
|
||||
removeScenery(core.game.node.scenery.Scenery(Scenery.CHEST_2857, CHEST_LOCATION))
|
||||
return@on true
|
||||
}
|
||||
|
||||
// chest with hazeel scroll. unlocks with key, opens, and contains the scroll.
|
||||
on(Scenery.CHEST_2857, IntType.SCENERY, "Search") { player, _ ->
|
||||
val hasScroll = hasAnItem(player, HAZEEL_SCROLL).container != null
|
||||
if (hasScroll) {
|
||||
sendDialogue(player, "You already have the scroll from this chest.")
|
||||
return@on true
|
||||
}
|
||||
addItemOrDrop(player, HAZEEL_SCROLL, 1)
|
||||
sendItemDialogue(player, HAZEEL_SCROLL, "Inside the chest you find the Scroll of Hazeel.")
|
||||
setQuestStage(player, Quests.HAZEEL_CULT, 5)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
// interact locations for the sewer valves
|
||||
override fun defineDestinationOverrides() {
|
||||
setDest(IntType.SCENERY, intArrayOf(SEWER_1), "turn-right", "turn-left") { _, _ ->
|
||||
return@setDest Location.create(2563, 3246, 0)
|
||||
}
|
||||
setDest(IntType.SCENERY, intArrayOf(SEWER_2), "turn-right", "turn-left") { _, _ ->
|
||||
return@setDest Location.create(2571, 3263, 0)
|
||||
}
|
||||
setDest(IntType.SCENERY, intArrayOf(SEWER_3), "turn-right", "turn-left") { _, _ ->
|
||||
return@setDest Location.create(2586, 3244, 0)
|
||||
}
|
||||
setDest(IntType.SCENERY, intArrayOf(SEWER_4), "turn-right", "turn-left") { _, _ ->
|
||||
return@setDest Location.create(2597, 3262, 0)
|
||||
}
|
||||
setDest(IntType.SCENERY, intArrayOf(SEWER_5), "turn-right", "turn-left") { _, _ ->
|
||||
return@setDest Location.create(2610, 3242, 0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val HAZEEL_CULT_VARP = 223
|
||||
|
||||
const val attrSewer1R = "quest:hazeelcult-sewer1"
|
||||
const val attrSewer2R = "quest:hazeelcult-sewer2"
|
||||
const val attrSewer3L = "quest:hazeelcult-sewer3"
|
||||
const val attrSewer4R = "quest:hazeelcult-sewer4"
|
||||
const val attrSewer5R = "quest:hazeelcult-sewer5"
|
||||
|
||||
const val attrHazeel = "/save:quest:hazeelcult-hazeel" // true when you agree to help Clivet
|
||||
const val attrCarnillean = "/save:quest:hazeelcult-carnillean" // true when you DON'T agree to help Clivet
|
||||
|
||||
const val CLIVET_NPC = NPCs.CLIVET_893
|
||||
const val CLIVET_RAFT = Scenery.RAFT_2849
|
||||
|
||||
const val CAVE_ENTRANCE = Scenery.CAVE_ENTRANCE_2852
|
||||
const val CLIVET_STAIRS = Scenery.STAIRS_2853
|
||||
|
||||
const val HAZEEL_SCROLL = Items.HAZEEL_SCROLL_2403
|
||||
|
||||
const val SEWER_1 = Scenery.SEWER_VALVE_2844 // Valve 1 - This is right across the bridge south of the Carnilleans' house. Turn it right.
|
||||
const val SEWER_2 = Scenery.SEWER_VALVE_2845 // Valve 2 - This is right behind Carnilleans' house. Turn it right.
|
||||
const val SEWER_3 = Scenery.SEWER_VALVE_2846 // Valve 3 - This is north of the cave. Turn it left.
|
||||
const val SEWER_4 = Scenery.SEWER_VALVE_2847 // Valve 4 - This is south of the zoo's penguin cage. Turn it right.
|
||||
const val SEWER_5 = Scenery.SEWER_VALVE_2848 // Valve 5 - Go southeast of the fourth valve, past the monk. Turn it right.
|
||||
|
||||
val SEWERS = intArrayOf(SEWER_1, SEWER_2, SEWER_3, SEWER_4, SEWER_5)
|
||||
|
||||
const val WARDROBE = Scenery.WARDROBE_2851
|
||||
|
||||
const val TURN_VALVE_ANIMATION = 4861
|
||||
|
||||
const val CRATE = Scenery.CRATE_34585
|
||||
const val CHEST_KEY = Items.CHEST_KEY_709
|
||||
const val POISON = Items.POISON_273
|
||||
const val COOKING_RANGE = Scenery.COOKING_RANGE_2859
|
||||
|
||||
const val SECRET_PASSAGE = Scenery.WALL_26940
|
||||
|
||||
val CHEST_LOCATION = Location(2565, 3272, 2)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import core.api.getQuestStage
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class HazeelCultistDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int, ): Boolean {
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
|
||||
when {
|
||||
(questStage in 1..99) -> when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hi.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.ANNOYED, "What? Who are you and why did you come here? It doesn't matter, leave now adventurer. While you still can.",).also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
(questStage == 100) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 1 }
|
||||
} else {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 2 }
|
||||
}
|
||||
}
|
||||
1 -> npcl(FacialExpression.FRIENDLY, "The adventurer returns! Glory to you for your efforts in bringing about the return of Lord Hazeel!").also { stage = END_DIALOGUE }
|
||||
2 -> npcl(FacialExpression.ANNOYED, "It's the meddler! The one who blew Jones' cover! I don't know why you came to this place but it is here that you will die.").also { stage++ }
|
||||
3 -> {
|
||||
end()
|
||||
npc.attack(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.HAZEEL_CULTIST_894)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import core.game.node.entity.npc.AbstractNPC
|
||||
import core.game.world.map.Location
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.*
|
||||
|
||||
/**
|
||||
* Hazeel is spawned in briefly during the Mahjarrat end of Hazeel Cult quest.
|
||||
*/
|
||||
|
||||
@Initializable
|
||||
class HazeelNPC(id: Int = 0, location: Location? = null, ) : AbstractNPC(id, location) {
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any, ): AbstractNPC = HazeelNPC(id, location)
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.HAZEEL_892)
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import core.api.getQuestStage
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class HenryetaDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int): Boolean {
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
|
||||
when {
|
||||
// stage 3 - poison poured in food (mahjarrat-only stage)
|
||||
(questStage == 3) -> {
|
||||
when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello. Are you okay?").also { stage++ }
|
||||
1 -> npcl(FacialExpression.SAD, "No, no I am very far from OK. Those hooligans slaughtered my precious Scruffy! I shall never recover! I am emotionally scarred for life!").also { stage = END_DIALOGUE }
|
||||
2 -> playerl(FacialExpression.NEUTRAL, "It WAS only a dog you know...").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 0 - unstarted
|
||||
// stage 1 - after talking to ceril
|
||||
// stage 2 - talk to clivet (set attr for carnillean)
|
||||
// stage 2 - talk to clivet (set attr for mahjarrat)
|
||||
// stage 4 - alomone either fought or he tells you he needs scroll
|
||||
// stage 5 - either returning the armour, or finding the scroll
|
||||
(questStage in 0..99) -> {
|
||||
when (stage) {
|
||||
0 -> npcl(FacialExpression.FRIENDLY, "Oh, hello. I'm afraid I'm not very presentable at the moment. I've been under incredible stress of late! It's ruining my complexion!").also { stage++ }
|
||||
1 -> playerl(FacialExpression.FRIENDLY, "Why? What's wrong?").also { stage++ }
|
||||
2 -> npcl(FacialExpression.FRIENDLY, "It's those awful cultists! I just don't feel safe here anymore!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
(questStage == 100) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 1 }
|
||||
} else {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 3 }
|
||||
}
|
||||
}
|
||||
|
||||
1 -> npcl(FacialExpression.HALF_ASKING, "Oh, it's you. I'm very disappointed that you never found those awful cultists. You're quite useless really, aren't you?").also { stage++ }
|
||||
2 -> playerl(FacialExpression.HAPPY, "Yup, I guess I am.").also { stage = END_DIALOGUE }
|
||||
|
||||
3 -> npcl(FacialExpression.FRIENDLY, "Hello again adventurer! Since you dealt with those horrible hooligan cultists, things have vastly improved for us around here!").also { stage++ }
|
||||
4 -> playerl(FacialExpression.HAPPY, "My pleasure, Lady Henryeta.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.HENRYETA_CARNILLEAN_889)
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc
|
||||
import core.api.getQuestStage
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.*
|
||||
|
||||
@Initializable
|
||||
class PhilipeDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun handle(
|
||||
componentID: Int,
|
||||
buttonID: Int,
|
||||
): Boolean {
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
|
||||
when {
|
||||
// stage 0 - unstarted
|
||||
(questStage == 0) -> {
|
||||
when (stage) {
|
||||
0 -> npcl(FacialExpression.CHILD_NORMAL, "Hello, how are you today?").also { stage++ }
|
||||
1 -> playerl(FacialExpression.FRIENDLY, "Good thank you. And yourself?").also { stage++ }
|
||||
2 -> npcl(FacialExpression.CHILD_NORMAL, "Fine and dandy.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 3 - poison poured in food (mahjarrat-only stage)
|
||||
(questStage == 3) -> {
|
||||
when (stage) {
|
||||
0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ }
|
||||
1 -> npcl(FacialExpression.CHILD_SAD, "Someone killed Scruffy. I liked Scruffy. He never told me off.").also { stage++ }
|
||||
2 -> playerl(FacialExpression.HALF_GUILTY, "Yeah... it's a real shame.").also { stage++ }
|
||||
3 -> npcl(FacialExpression.CHILD_SAD, "I want my mommy.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 1 - after talking to ceril
|
||||
// stage 2 - talk to clivet (set attr for carnillean)
|
||||
// stage 2 - talk to clivet (set attr for mahjarrat)
|
||||
// stage 4 - alomone either fought or he tells you he needs scroll
|
||||
// stage 5 - either returning the armour, or finding the scroll
|
||||
(questStage in 1..99) -> {
|
||||
when (stage) {
|
||||
0 -> npcl(FacialExpression.CHILD_NORMAL, "Mommy said you're here to kill all the nasty people that keep breaking in.").also { stage++ }
|
||||
1 -> playerl(FacialExpression.FRIENDLY, "Something like that.").also { stage++ }
|
||||
2 -> npcl(FacialExpression.CHILD_FRIENDLY, "Can I watch?").also { stage++ }
|
||||
3 -> playerl(FacialExpression.FRIENDLY, "No!").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
|
||||
// stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel
|
||||
(questStage == 100) -> when (stage) {
|
||||
0 -> {
|
||||
if (mahjarratArc(player)) {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 1 }
|
||||
} else {
|
||||
playerl(FacialExpression.FRIENDLY, "Hello youngster.").also { stage = 4 }
|
||||
}
|
||||
}
|
||||
|
||||
1 -> npcl(FacialExpression.CHILD_NORMAL, "What have you brought me? I want some more toys!").also { stage++ }
|
||||
2 -> playerl(FacialExpression.FRIENDLY, "I'm afraid I don't have any toys.").also { stage++ }
|
||||
3 -> npcl(FacialExpression.CHILD_SAD, "Toys! I want toys!").also { stage = END_DIALOGUE }
|
||||
|
||||
4 -> npcl(FacialExpression.CHILD_NORMAL, "Daddy says you don't like Mr. Jones. Mr. Jones is nice. He brings me toys and sweets.").also { stage++ }
|
||||
5 -> playerl(FacialExpression.FRIENDLY, "Jones is a bad man, Philipe.").also { stage++ }
|
||||
6 -> npcl(FacialExpression.CHILD_NORMAL, "You're a bad " + (if (player.isMale) "man" else "lady") + " I don't like you!").also { stage++ }
|
||||
7 -> playerl(FacialExpression.FRIENDLY, "I'll try and console myself about that disappointment somehow.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray = intArrayOf(NPCs.PHILIPE_CARNILLEAN_888)
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package content.region.kandarin.ardougne.quest.hazeelcult
|
||||
|
||||
import content.data.Quests
|
||||
import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.carnilleanArc
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialogueFile
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
/**
|
||||
* This dialogue is triggered at stage 5 of Hazeel Cult during the Carnillean Arc by searching the wardrobe for evidence.
|
||||
*/
|
||||
|
||||
@Initializable
|
||||
class WardrobeRevealDialogue (player: Player? = null) : DialoguePlugin(player) {
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
openDialogue(player, WardrobeRevealDialogueFile())
|
||||
return true
|
||||
}
|
||||
override fun newInstance(player: Player): DialoguePlugin {
|
||||
return WardrobeRevealDialogue(player)
|
||||
}
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf() // dialogue is triggered manually
|
||||
}
|
||||
}
|
||||
|
||||
class WardrobeRevealDialogueFile : DialogueFile() {
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT)
|
||||
when {
|
||||
(questStage == 5 && carnilleanArc(player!!)) -> {
|
||||
when (stage) {
|
||||
// note that it's authentic for the 'wardrobe' to be referred to as 'cupboard' in the dialogue. it used to be a cupboard pre-graphical update. https://youtu.be/CH7i06Noc18?si=zAdxBNlIS92cseBR&t=606
|
||||
0 -> sendDialogue(player!!, "You search the cupboard thoroughly. You find a bottle of poison and a mysterious amulet. You pass your discovery on to Ceril.").also { stage++ }
|
||||
1 -> sendPlayerDialogue(player!!, "Ceril!").also { stage++ }
|
||||
2 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "What do you want now you scoundrel?", FacialExpression.ANGRY).also { stage++ }
|
||||
3 -> sendPlayerDialogue(player!!, "Look what I've found in the cupboard!", FacialExpression.ANGRY).also { stage++ }
|
||||
4 -> sendDialogue(player!!, "You hand Ceril the bottle of poison.").also { stage++ }
|
||||
5 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "I... I don't believe it! Poison! JONES!").also { stage++ }
|
||||
6 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "You called m'lud?").also { stage++ }
|
||||
7 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "JONES! Just WHAT is this POISON doing here???").also { stage++ }
|
||||
8 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "P-p-poison??? Yeah... uh... well, hmm... Oh, it's for Killing rats.").also { stage++ } // '...for Killing...' is authentic
|
||||
9 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "Yes, that's right. Killing rats. I am but a loyal butler sir.").also { stage++ }
|
||||
10 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "Rats eh? Yes, fair enough. Sorry to have doubted you old boy.").also { stage++ }
|
||||
11 -> sendPlayerDialogue(player!!, "Then how about this?").also { stage++ }
|
||||
12 -> sendDialogue(player!!, "You show Ceril the amulet from the cupboard.").also { stage++ }
|
||||
13 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "Wh-what??? I've seen this amulet before... The thieves that broke in... One of them was wearing an identical amulet! Jones! I don't believe it!").also { stage++ }
|
||||
14 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "Jones! We trusted you! We took you into our home!").also { stage++ }
|
||||
15 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "You senile old fool... It was all too easy! I should have killed you and your pathetic family weeks ago!", FacialExpression.ANGRY).also { stage++ }
|
||||
16 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "To think we took you in and trusted you... and this is how you repay us...").also { stage++ }
|
||||
17 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "GUARD!").also { stage++ }
|
||||
18 -> sendDialogue(player!!, "A guard rushes into the house.").also { stage++ }
|
||||
19 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "Take him away. Attempted murder and burglary.").also { stage++ }
|
||||
20 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "Don't think this is the last you have heard of me Ceril. You and your family will pay dearly for this. GLORY TO HAZEEL! ALL PRAISE ZAMORAK!").also { stage++ }
|
||||
21 -> sendNPCDialogue(player!!, NPCs.GUARD_887, "Now now. Come along quietly. There'll be no revenge where YOU'RE going - Port Sarim Jail.").also { stage++ }
|
||||
22 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "It looks like I am indebted to you sirrah!").also { stage++ }
|
||||
23 -> sendPlayerDialogue(player!!, "No problem. We all make mistakes.").also { stage++ }
|
||||
24 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "But if it weren't for you... My whole family... we could have been... I apologize for my harshness before.").also { stage++ }
|
||||
25 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "The very least I can do is to reward you for your noble efforts, and to offer my sincerest apologies as a Lord and a gentleman.").also { stage++ }
|
||||
26 -> sendPlayerDialogue(player!!, "Thank you Lord Ceril.").also { stage++ }
|
||||
27 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "No, no, thank YOU! Feel free to stop by anytime adventurer!"
|
||||
).also {
|
||||
end()
|
||||
finishQuest(player!!, Quests.HAZEEL_CULT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(questStage in 0..100) -> {
|
||||
when(stage) {
|
||||
0 -> sendDialogue(player!!, "You search the cupboard thoroughly but find nothing of interest.").also {stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,9 +96,15 @@ class PlagueCityListeners : InteractionListener {
|
|||
addItem(player, Items.ASHES_592)
|
||||
}
|
||||
else{
|
||||
sendItemDialogue(player, Items.A_MAGIC_SCROLL_1505, "You memorise what is written on the scroll.")
|
||||
sendDialogue(player, "You can now cast the Ardougne Teleport spell provided you have the required runes and magic level.")
|
||||
setAttribute(player, ARDOUGNE_TELE_ATTRIBUTE, true)
|
||||
openDialogue(player, object : DialogueFile() {
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
when (stage) {
|
||||
0 -> sendItemDialogue(player, Items.A_MAGIC_SCROLL_1505, "You memorise what is written on the scroll.").also { stage++ }
|
||||
1 -> sendDialogue(player, "You can now cast the Ardougne Teleport spell provided you have the required runes and magic level.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
|
@ -356,9 +362,8 @@ class PlagueCityListeners : InteractionListener {
|
|||
|
||||
on(Scenery.DOOR_2054, IntType.SCENERY, "open"){ player, node ->
|
||||
if (isQuestComplete(player, Quests.PLAGUE_CITY)){
|
||||
DoorActionHandler.handleDoor(player, node as SceneryNode)
|
||||
}
|
||||
else{
|
||||
DoorActionHandler.handleAutowalkDoor(player, node as SceneryNode)
|
||||
} else {
|
||||
sendMessage(player, "This door is locked")
|
||||
}
|
||||
return@on true
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -141,8 +142,9 @@ class BindingBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.BINDING_BOOK_730, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.BINDING_BOOK_730, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -330,8 +331,9 @@ class GiannesCookBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.GIANNES_COOK_BOOK_2167, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.GIANNES_COOK_BOOK_2167, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -47,8 +48,9 @@ class ShamansTomeBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.SHAMANS_TOME_729, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.SHAMANS_TOME_729, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -87,8 +88,9 @@ class DMCManual : InteractionListener {
|
|||
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.INSTRUCTION_MANUAL_5, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.INSTRUCTION_MANUAL_5, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -131,8 +132,9 @@ class TranslationBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.TRANSLATION_BOOK_784, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.TRANSLATION_BOOK_784, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package content.region.kandarin.quest.observatoryquest
|
|||
|
||||
import content.global.handlers.iface.*
|
||||
import core.ServerConstants
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -161,8 +162,9 @@ class AstronomyBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.ASTRONOMY_BOOK_600, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.ASTRONOMY_BOOK_600, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import content.global.handlers.iface.BookLine
|
|||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.setAttribute
|
||||
import core.api.storeBookInHouse
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
|
|
@ -152,8 +153,9 @@ class BaxtorianBook : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.BOOK_ON_BAXTORIAN_292, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.BOOK_ON_BAXTORIAN_292, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import core.game.interaction.InteractionListener
|
|||
import core.game.node.entity.player.Player
|
||||
import org.rs09.consts.Items
|
||||
import content.data.Quests
|
||||
import core.api.*
|
||||
|
||||
/**
|
||||
* Battered book handler for the Elemental Workshop I quest
|
||||
|
|
@ -31,8 +32,9 @@ class BatteredBookHandler : InteractionListener {
|
|||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.BATTERED_BOOK_2886, IntType.ITEM, "read") { player, _ ->
|
||||
on(Items.BATTERED_BOOK_2886, IntType.ITEM, "read") { player, node ->
|
||||
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
|
||||
storeBookInHouse(player, node)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue