Implemented brewing

This commit is contained in:
Bishop
2026-04-01 14:33:41 +00:00
committed by Ryan
parent 187c513044
commit 2bad80f141
32 changed files with 1781 additions and 133 deletions
+3 -3
View File
@@ -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",
+46 -46
View File
@@ -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)",
@@ -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",
+1
View File
@@ -76115,6 +76115,7 @@
"id": "2308"
},
{
"examine": "In his former life an expert brewer.",
"name": "Metarialus",
"id": "2322"
},
+4
View File
@@ -5547,6 +5547,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}-"
@@ -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))),
@@ -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),
@@ -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
}
}
}
@@ -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(){
@@ -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
}
@@ -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
}
}
@@ -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) )
;
@@ -70,6 +70,7 @@ class SeedlingListener : InteractionListener {
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)
@@ -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 -> {
@@ -1,51 +1,110 @@
package content.region.misc.keldagrim.dialogue
import core.game.dialogue.DialoguePlugin
import core.game.dialogue.FacialExpression
import core.game.dialogue.Topic
import core.game.node.entity.player.Player
import core.api.*
import core.game.dialogue.ChatAnim
import core.game.dialogue.DialogueLabeller
import core.game.dialogue.DialogueOption
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.npc.NPC
import core.game.node.item.Item
import core.plugin.Initializable
import core.tools.END_DIALOGUE
import core.tools.START_DIALOGUE
import org.rs09.consts.Items
import org.rs09.consts.NPCs
@Initializable
class BlandebirDialogue(player: Player? = null) : DialoguePlugin(player) {
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
when(stage){
START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello, I wonder if you could help me on this whole brewing thing...").also { stage++ }
1 -> npcl(FacialExpression.OLD_DEFAULT, "I might be able to - what do you need to know?").also { stage++ }
2 -> showTopics(
Topic(FacialExpression.FRIENDLY, "How do I brew ales?", 3),
Topic(FacialExpression.FRIENDLY, "How do I brew cider?", 6),
Topic(FacialExpression.FRIENDLY, "What do I do once my ale has matured?", 8),
Topic(FacialExpression.FRIENDLY, "Do you have any spare ale yeast?", 9),
Topic(FacialExpression.FRIENDLY, "That's all I need to know, thanks.", END_DIALOGUE),
class BlandebirDialogue : InteractionListener {
override fun defineListeners() {
on(NPCs.BLANDEBIR_2321, IntType.NPC, "talk-to") { player, node ->
DialogueLabeller.open(player, BlandebirDialogueFile(), node as NPC)
return@on true
}
}
}
class BlandebirDialogueFile : DialogueLabeller() {
override fun addConversation() {
assignToIds(NPCs.BLANDEBIR_2321)
player(ChatAnim.FRIENDLY, "Hello, I wonder if you could help me on this whole brewing thing...")
npc(ChatAnim.OLD_DEFAULT, "I might be able to - what do you need to know?")
goto("main_menu")
label("main_menu") {
options(
DialogueOption("how_brew_ales", "How do I brew ales?", expression = ChatAnim.HALF_ASKING, spokenText = "How do I brew ales?"),
DialogueOption("how_brew_cider", "How do I brew cider?", expression = ChatAnim.HALF_ASKING, spokenText = "How do I brew cider?"),
DialogueOption("what_when_matured", "What do I do once my ale has matured?", expression = ChatAnim.HALF_ASKING, spokenText = "What do I do once my ale has matured?"),
DialogueOption("buy_yeast", "Do you have any spare ale yeast?", expression = ChatAnim.HALF_ASKING, spokenText = "Do you have any spare ale yeast?"),
DialogueOption("end", "That's all I need to know, thanks.")
)
3 -> npcl(FacialExpression.OLD_DEFAULT, "Well first off you need to fill the vat with water - two bucketfuls should do the trick. Then you'll need to put in two handfuls of barley malt - that's roasted barley by the way.").also { stage++ }
4 -> npcl(FacialExpression.OLD_DEFAULT, "After that you'll be putting your main ingredient in - this will decide which ale it is you're brewing. There should be some good guides around with recipes in.").also { stage++ }
5 -> npcl(FacialExpression.OLD_DEFAULT, "Lastly you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.").also {
stage = 2
}
6 -> npcl(FacialExpression.OLD_DEFAULT, "First you'll need some apples. You'll need to crush them using this cider-press - four apples should make a full bucket of apple-mush. Take four buckets of mush and fill up the vat.").also { stage++ }
7 -> npcl(FacialExpression.OLD_DEFAULT, "After that you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.").also {
stage = 2
}
8 -> npcl(FacialExpression.OLD_DEFAULT, "Well, once you've got a full vat of the good stuff, just turn the valve and your barrel will fill up with eight pints of whatever your chosen tipple is. Mind it's an empty barrel, though.").also {
stage = 2
}
9 -> npcl(FacialExpression.OLD_DEFAULT, "Well, as a matter of fact I do, although I wouldn't describe it as spare. This ale yeast I've got is the best money can buy, but if you've got a pot I'll fill it for you for 25GP - very cheap as it happens.").also {
stage = 2
}
}
return true
}
override fun newInstance(player: Player?): DialoguePlugin {
return BlandebirDialogue(player)
label("how_brew_ales") {
npc(ChatAnim.OLD_DEFAULT, "Well first off you need to fill the vat with water - two bucketfuls should do the trick. Then you'll need to put in two handfuls of barley malt - that's roasted barley by the way.")
npc(ChatAnim.OLD_DEFAULT, "After that you'll be putting your main ingredient in - this will decide which ale it is you're brewing. There should be some good guides around with recipes in.")
npc(ChatAnim.OLD_DEFAULT, "Lastly you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.")
goto("main_menu")
}
override fun getIds(): IntArray {
return intArrayOf(NPCs.BLANDEBIR_2321)
label("how_brew_cider") {
npc(ChatAnim.OLD_DEFAULT, "First you'll need some apples. You'll need to crush them using this cider-press - four apples should make a full bucket of apple-mush. Take four buckets of mush and fill up the vat.")
npc(ChatAnim.OLD_DEFAULT, "After that you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.")
goto("main_menu")
}
label("what_when_matured") {
npc(ChatAnim.OLD_DEFAULT, "Well, once you've got a full vat of the good stuff, just turn the valve and your barrel will fill up with eight pints of whatever your chosen tipple is. Mind it's an empty barrel, though.")
goto("main_menu")
}
label("buy_yeast") {
npc(ChatAnim.OLD_DEFAULT, "Well, as a matter of fact I do, although I wouldn't describe it as spare. This ale yeast I've got is the best money can buy, but if you've got a pot I'll fill it for you for 25GP - very cheap as it happens.")
options(
DialogueOption("confirm_buy", "That's a good deal - please fill my pot with ale yeast for 25GP."),
DialogueOption("end", "No, that's too much I'm afraid.")
)
}
label("confirm_buy") {
exec { player, npc ->
if (!inInventory(player, Items.EMPTY_POT_1931, 1)) {
loadLabel(player, "no_pot")
} else if (!inInventory(player, Items.COINS_995, 25)) {
loadLabel(player, "no_coins")
} else {
loadLabel(player, "process_purchase")
}
}
}
label("no_pot") {
npc(ChatAnim.OLD_DEFAULT, "I'm afraid you'll need a pot for me to put the yeast in.")
goto("end")
}
label("no_coins") {
line("You can not afford that.")
goto("end")
}
label("process_purchase") {
exec { player, npc ->
val emptyPots = amountInInventory(player, Items.EMPTY_POT_1931)
val coins = amountInInventory(player, Items.COINS_995)
val canAfford = coins / 25
val quantity = minOf(emptyPots, canAfford)
if (removeItem(player, Item(Items.EMPTY_POT_1931, quantity)) && removeItem(player, Item(Items.COINS_995, quantity * 25))) {
addItem(player, Items.ALE_YEAST_5767, quantity)
}
}
goto("main_menu")
}
label("end") {
}
}
}
@@ -0,0 +1,127 @@
package content.region.morytania.phas.dialogue
import core.api.*
import core.game.dialogue.ChatAnim
import core.game.dialogue.DialogueLabeller
import core.game.dialogue.DialogueOption
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.npc.NPC
import core.game.node.item.Item
import core.plugin.Initializable
import org.rs09.consts.Items
import org.rs09.consts.NPCs
@Initializable
class MetarialusDialogue : InteractionListener {
override fun defineListeners() {
on(NPCs.METARIALUS_2322, IntType.NPC, "talk-to") { player, node ->
if (inEquipment(player, Items.GHOSTSPEAK_AMULET_552) || inEquipment(player, Items.GHOSTSPEAK_AMULET_4250)) {
DialogueLabeller.open(player, MetarialusDialogueFile(), node as NPC)
return@on true
} else {
DialogueLabeller.open(player, MetarialusDialogueNoGhostSpeakFile(), node as NPC)
return@on true
}
}
}
}
class MetarialusDialogueFile : DialogueLabeller() {
override fun addConversation() {
assignToIds(NPCs.METARIALUS_2322)
player(ChatAnim.FRIENDLY, "Hello, I wonder if you could help me on this whole brewing thing...")
npc(ChatAnim.NEUTRAL, "I might be able to - what do you need to know?")
goto("main_menu")
label("main_menu") {
options(
DialogueOption("how_brew_ales", "How do I brew ales?", expression = ChatAnim.HALF_ASKING, spokenText = "How do I brew ales?"),
DialogueOption("how_brew_cider", "How do I brew cider?", expression = ChatAnim.HALF_ASKING, spokenText = "How do I brew cider?"),
DialogueOption("what_when_matured", "What do I do once my ale has matured?", expression = ChatAnim.HALF_ASKING, spokenText = "What do I do once my ale has matured?"),
DialogueOption("buy_yeast", "Do you have any spare ale yeast?", expression = ChatAnim.HALF_ASKING, spokenText = "Do you have any spare ale yeast?"),
DialogueOption("end", "That's all I need to know, thanks.")
)
}
label("how_brew_ales") {
npc(ChatAnim.NEUTRAL, "Well first off you need to fill the vat with water - two bucketfuls should do the trick. Then you'll need to put in two handfuls of barley malt - that's roasted barley by the way.")
npc(ChatAnim.NEUTRAL, "After that you'll be putting your main ingredient in - this will decide which ale it is you're brewing. There should be some good guides around with recipes in.")
npc(ChatAnim.NEUTRAL, "Lastly you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.")
goto("main_menu")
}
label("how_brew_cider") {
npc(ChatAnim.NEUTRAL, "First you'll need some apples. You'll need to crush them using this cider-press - four apples should make a full bucket of apple-mush. Take four buckets of mush and fill up the vat.")
npc(ChatAnim.NEUTRAL, "After that you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.")
goto("main_menu")
}
label("what_when_matured") {
npc(ChatAnim.NEUTRAL, "Well, once you've got a full vat of the good stuff, just turn the valve and your barrel will fill up with eight pints of whatever your chosen tipple is. Mind it's an empty barrel, though.")
goto("main_menu")
}
label("buy_yeast") {
npc(ChatAnim.NEUTRAL, "Well, as a matter of fact I do, although I wouldn't describe",
"it as spare. This ale yeast I've got is the best money can",
"buy, but if you've got a pot I'll fill it for you for",
"5 ecto-tokens - very cheap as it happens.")
options(
DialogueOption("confirm_buy", "That's a good deal - please fill my pot with ale yeast for 5 ecto-tokens."),
DialogueOption("end", "No, that's too much I'm afraid.")
)
}
label("confirm_buy") {
exec { player, npc ->
if (!inInventory(player, Items.EMPTY_POT_1931, 1)) {
loadLabel(player, "no_pot")
} else if (!inInventory(player, Items.ECTO_TOKEN_4278, 5)) {
loadLabel(player, "no_tokens")
} else {
loadLabel(player, "process_purchase")
}
}
}
label("no_pot") {
npc(ChatAnim.NEUTRAL, "I'm afraid you'll need a pot for me to put the yeast in.")
goto("end")
}
label("no_tokens") {
line("You can not afford that.")
goto("end")
}
label("process_purchase") {
exec { player, npc ->
val emptyPots = amountInInventory(player, Items.EMPTY_POT_1931)
val tokens = amountInInventory(player, Items.ECTO_TOKEN_4278)
val canAfford = tokens / 5
val quantity = minOf(emptyPots, canAfford)
if (removeItem(player, Item(Items.EMPTY_POT_1931, quantity)) && removeItem(player, Item(Items.ECTO_TOKEN_4278, quantity * 5))) {
addItem(player, Items.ALE_YEAST_5767, quantity)
}
}
goto("main_menu")
}
label("end") {
}
}
}
class MetarialusDialogueNoGhostSpeakFile : DialogueLabeller() {
override fun addConversation() {
assignToIds(NPCs.METARIALUS_2322)
npc(ChatAnim.WORRIED, "Wooo Woo ooo wooowoo!") // Placeholder taken from restless ghost, can't find source for his no-ghostspeak text
}
}
+1 -1
View File
@@ -3027,7 +3027,7 @@ fun queueScript(entity: Entity, delay: Int = 1, strength: QueueStrength = QueueS
}
/**
* Sets the clock to the value of WORLD_TICSK + ticks.
* Sets the clock to the value of WORLD_TICKS + ticks.
* @param entity the entity whose clock we are updating
* @param clock the clock we are updating. Please use [core.game.interaction.Clocks] for this argument.
* @param ticks the number of ticks to delay by
@@ -1,6 +1,7 @@
package core.game.system.command.sets
import content.global.handlers.iface.*
import content.global.skill.cooking.fermenting.BrewGrowth
import content.global.skill.farming.timers.*
import content.minigame.fishingtrawler.TrawlerLoot
import content.region.misthalin.draynor.quest.anma.AnmaCutscene
@@ -32,6 +33,7 @@ import core.tools.StringUtils
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.rs09.consts.Components
import org.rs09.consts.Items
import java.awt.HeadlessException
import java.awt.Toolkit
import java.awt.datatransfer.StringSelection
@@ -698,6 +700,64 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){
notify(player, "Setting plaques read to: ${args[1]}")
}
define("brewkit", Privilege.ADMIN, "brewkit <lt>0-10<gt>",
"Give yourself everything you need to brew ales."){ player, args ->
if (args.size == 1) {
sendMessage(player, "Kit numbers: 0 - Dwarven Stout, 1 - Asgarnian Ale, 2 - Greenman's Ale,")
sendMessage(player, "Kit numbers: 3 - Wizard's Mind Bomb, 4 - Dragon Bitter, 5 - Moonlight Mead,")
sendMessage(player, "Kit numbers: 6 - Axeman's Folly, 7 - Chef's Delight, 8 - Slayer's Respite,")
sendMessage(player, "Kit numbers: 9 - Cider, 10 - Kelda Stout")
return@define
}
val item =
when (args[1].toInt()) {
0 -> Items.HAMMERSTONE_HOPS_5994
1 -> Items.ASGARNIAN_HOPS_5996
2 -> Items.CLEAN_HARRALANDER_255
3 -> Items.YANILLIAN_HOPS_5998
4 -> Items.KRANDORIAN_HOPS_6000
5 -> Items.MUSHROOM_6004
6 -> Items.OAK_ROOTS_6043
7 -> Items.CHOCOLATE_DUST_1975
8 -> Items.WILDBLOOD_HOPS_6002
9 -> Items.APPLE_MUSH_5992
10 -> Items.KELDA_HOPS_6113
else -> {
reject(player, "Invalid brew kit specified.")
return@define
}
}
addItemOrDrop(player, Items.BUCKET_OF_WATER_1929, if(args[1].toInt() == 9) 0 else 2)
addItemOrDrop(player, Items.BARLEY_MALT_6008, if(args[1].toInt() == 9) 0 else 2)
addItemOrDrop(player, item, if(args[1].toInt() == 6 || args[1].toInt() == 10) 1 else 4)
addItemOrDrop(player, Items.ALE_YEAST_5767)
return@define
}
define("brew", Privilege.ADMIN, "brew <lt>mode<gt>",
"0: force natural step, 1: force good step without maturity, 2: force good step with maturity, 3: force bad step"){ player, args ->
var forceStep = false
var forceGood = false
var forceMature = false
var forceBad = false
if (args.size == 1){
forceStep = true
}
if (args.size != 1){
when(args[1].toIntOrNull()){
0 -> forceStep = true
1 -> forceGood = true
2 -> forceMature = true
3 -> forceBad = true
else -> reject(player, "${args[1]} is not a valid option.")
}
}
val vats = getOrStartTimer<BrewGrowth>(player).getVats()
for (vat in vats){
vat.brew(forceStep, forceGood, forceMature, forceBad)
}
}
}
fun setupScrollInterface(player: Player, title: String): Int {
@@ -1,11 +1,13 @@
package core.game.system.command.sets
import core.api.log
import core.game.component.Component
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.quest.QuestRepository
import core.plugin.Initializable
import core.game.system.command.Privilege
import core.game.world.repository.Repository
import core.tools.Log
@Initializable
class QuestCommandSet : CommandSet(Privilege.ADMIN){
@@ -15,7 +17,12 @@ class QuestCommandSet : CommandSet(Privilege.ADMIN){
*/
define("allquest", description = "Instantly completes every quest for your account."){player,_->
for (quest in QuestRepository.getQuests().values) {
try {
quest.finish(player)
} catch (e: Exception){
e.message?.let { log(this.javaClass, Log.WARN, it) }
continue
}
}
}