From 33a81b6ce96db0c500519b2eb939d4ae4606e05e Mon Sep 17 00:00:00 2001 From: skelsoft <9147770-skelsoft@users.noreply.gitlab.com> Date: Fri, 13 Aug 2021 12:40:05 +0000 Subject: [PATCH 01/12] Examine: Pizzas, Pies, Barrows Gear and Damaged Books --- Server/data/configs/item_configs.json | 314 +++++++++++++++++--------- 1 file changed, 211 insertions(+), 103 deletions(-) diff --git a/Server/data/configs/item_configs.json b/Server/data/configs/item_configs.json index 62d72fee2..e06274c39 100644 --- a/Server/data/configs/item_configs.json +++ b/Server/data/configs/item_configs.json @@ -24829,6 +24829,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "366", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Incomplete pizza", "tradeable": "true", @@ -24837,7 +24838,7 @@ }, { "ge_buy_limit": "1000", - "examine": "This needs cooking", + "examine": "This needs cooking.", "grand_exchange_price": "1141", "durability": null, "name": "Uncooked pizza", @@ -24850,6 +24851,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1141", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Uncooked pizza", "tradeable": "true", @@ -24859,7 +24861,7 @@ { "shop_price": "200", "ge_buy_limit": "1000", - "examine": "A cheese and tomato pizza. (Whole)Half of this plain pizza has been eaten. (Half)", + "examine": "A cheese and tomato pizza.", "grand_exchange_price": "1357", "durability": null, "name": "Plain pizza", @@ -24873,6 +24875,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1357", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Plain pizza", "tradeable": "true", @@ -24882,7 +24885,7 @@ { "shop_price": "200", "ge_buy_limit": "1000", - "examine": "A cheese and tomato pizza. (Whole)Half of this plain pizza has been eaten. (Half)", + "examine": "Half of this plain pizza has been eaten.", "grand_exchange_price": "238", "durability": null, "name": "1/2 plain pizza", @@ -24896,6 +24899,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "238", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "1/2 plain pizza", "tradeable": "true", @@ -24904,7 +24908,7 @@ }, { "ge_buy_limit": "1000", - "examine": "A pizza with bits of meat on it. (Whole) Half of this meat pizza has been eaten. (Half)", + "examine": "A pizza with bits of meat on it.", "grand_exchange_price": "1012", "durability": null, "name": "Meat pizza", @@ -24918,6 +24922,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1012", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Meat pizza", "tradeable": "true", @@ -24926,7 +24931,7 @@ }, { "ge_buy_limit": "1000", - "examine": "A pizza with bits of meat on it. (Whole) Half of this meat pizza has been eaten. (Half)", + "examine": "Half of this meat pizza has been eaten.", "grand_exchange_price": "339", "durability": null, "name": "1/2 meat pizza", @@ -24940,6 +24945,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "339", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "1/2 meat pizza", "tradeable": "true", @@ -24948,7 +24954,7 @@ }, { "ge_buy_limit": "1000", - "examine": "A pizza with anchovies. (Whole) Half of this anchovy pizza has been eaten. (Half)", + "examine": "A pizza with anchovies.", "grand_exchange_price": "1573", "durability": null, "name": "Anchovy pizza", @@ -24962,6 +24968,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1573", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Anchovy pizza", "tradeable": "true", @@ -24970,7 +24977,7 @@ }, { "ge_buy_limit": "1000", - "examine": "A pizza with anchovies. (Whole) Half of this anchovy pizza has been eaten. (Half)", + "examine": "Half of this anchovy pizza has been eaten.", "grand_exchange_price": "44", "durability": null, "name": "1/2 anchovy pizza", @@ -24984,6 +24991,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "44", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "1/2 anchovy pizza", "tradeable": "true", @@ -24992,7 +25000,7 @@ }, { "ge_buy_limit": "1000", - "examine": "A pizza with bits of pineapple on it. (Whole)Half of this pineapple pizza has been eaten. (Half)", + "examine": "A pizza with bits of pineapple on it.", "grand_exchange_price": "1469", "durability": null, "name": "Pineapple pizza", @@ -25006,6 +25014,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1469", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Pineapple pizza", "tradeable": "true", @@ -25015,6 +25024,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "156", + "examine": "Half of this pineapple pizza has been eaten.", "durability": null, "name": "1/2 p'apple pizza", "archery_ticket_price": "0", @@ -25023,6 +25033,7 @@ { "ge_buy_limit": "100", "grand_exchange_price": "156", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "1/2 p'apple pizza", "tradeable": "true", @@ -25030,6 +25041,7 @@ "id": "2304" }, { + "examine": "Oh dear!", "durability": null, "name": "Burnt pizza", "tradeable": "true", @@ -25147,6 +25159,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "781", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Uncooked apple pie", "tradeable": "true", @@ -25169,6 +25182,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "747", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Uncooked meat pie", "tradeable": "true", @@ -25191,6 +25205,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "802", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Uncooked berry pie", "tradeable": "true", @@ -25200,7 +25215,7 @@ { "shop_price": "140", "ge_buy_limit": "1000", - "examine": "Mmm Apple pie. (Whole)Mmm half an apple pie. (Half)", + "examine": "Mmm Apple pie.", "grand_exchange_price": "246", "durability": null, "name": "Apple pie", @@ -25214,6 +25229,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "246", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Apple pie", "tradeable": "true", @@ -25222,7 +25238,7 @@ }, { "ge_buy_limit": "1000", - "examine": "Looks tasty. (Whole)So tasty I kept some for later. (Half)", + "examine": "Looks tasty.", "grand_exchange_price": "534", "durability": null, "name": "Redberry pie", @@ -25236,6 +25252,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "534", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Redberry pie", "tradeable": "true", @@ -25246,6 +25263,7 @@ "shop_price": "16", "ge_buy_limit": "1000", "grand_exchange_price": "160", + "examine": "Not for vegetarians.", "durability": null, "name": "Meat pie", "tradeable": "true", @@ -25255,6 +25273,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "160", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Meat pie", "tradeable": "true", @@ -25262,6 +25281,7 @@ "id": "2328" }, { + "examine": "I think I left it on the stove too long.", "durability": null, "name": "Burnt pie", "tradeable": "true", @@ -25272,6 +25292,7 @@ "shop_price": "16", "ge_buy_limit": "1000", "grand_exchange_price": "174", + "examine": "Half of it is suitable for vegetarians.", "durability": null, "name": "Half a meat pie", "tradeable": "true", @@ -25281,6 +25302,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "174", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Half a meat pie", "tradeable": "true", @@ -25289,7 +25311,7 @@ }, { "ge_buy_limit": "1000", - "examine": "Looks tasty. (Whole)So tasty I kept some for later. (Half)", + "examine": "So tasty I kept some for later.", "grand_exchange_price": "248", "durability": null, "name": "Half a redberry pie", @@ -25303,6 +25325,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "248", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Half a redberry pie", "tradeable": "true", @@ -25312,7 +25335,7 @@ { "shop_price": "140", "ge_buy_limit": "1000", - "examine": "Mmm Apple pie. (Whole)Mmm half an apple pie. (Half)", + "examine": "Mmm half an apple pie.", "grand_exchange_price": "146", "durability": null, "name": "Half an apple pie", @@ -25326,6 +25349,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "146", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Half an apple pie", "tradeable": "true", @@ -35034,7 +35058,7 @@ "durability": null, "name": "Cooked slimy eel", "tradeable": "false", - "destroy": "true", + "destroy": "false", "weight": "2", "archery_ticket_price": "0", "id": "3381" @@ -38788,7 +38812,7 @@ }, { "shop_price": "5000", - "examine": "An incomplete book of Saradomin/Guthix/Zamorak/Bandos/ArmadylAn incomplete ancient book", + "examine": "An incomplete book of Saradomin.", "durability": null, "name": "Damaged book", "low_alchemy": "80", @@ -38814,7 +38838,7 @@ }, { "shop_price": "5000", - "examine": "An incomplete book of Saradomin/Guthix/Zamorak/Bandos/ArmadylAn incomplete ancient book", + "examine": "An incomplete book of Zamorak.", "durability": null, "name": "Damaged book", "low_alchemy": "80", @@ -38839,7 +38863,7 @@ }, { "shop_price": "5000", - "examine": "An incomplete book of Saradomin/Guthix/Zamorak/Bandos/ArmadylAn incomplete ancient book", + "examine": "An incomplete book of Guthix.", "durability": null, "name": "Damaged book", "low_alchemy": "80", @@ -45780,7 +45804,7 @@ "remove_head": "true", "requirements": "{1,70}-{6,70}", "ge_buy_limit": "10", - "examine": "Ahrim The Blighted's Hood.", + "examine": "Ahrim the Blighted's leather hood.", "durability": null, "low_alchemy": "5200", "high_alchemy": "7800", @@ -45797,6 +45821,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "94300", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Ahrim's hood", "tradeable": "true", @@ -45836,6 +45861,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "69500", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Ahrim's staff", "tradeable": "true", @@ -45863,6 +45889,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "1000000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Ahrim's robetop", "tradeable": "true", @@ -45889,6 +45916,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "1700000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Ahrim's robeskirt", "tradeable": "true", @@ -45899,7 +45927,7 @@ "remove_head": "true", "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Dharok The Wretched's Helmet", + "examine": "Dharok the Wretched's helm.", "durability": "1", "low_alchemy": "41200", "high_alchemy": "61800", @@ -45916,6 +45944,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "1300000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Dharok's helm", "tradeable": "true", @@ -45925,7 +45954,7 @@ { "ge_buy_limit": "10", "turn90cw_anim": "821", - "examine": "Dharok the Wretched's Greataxe.", + "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", "low_alchemy": "83200", "turn90ccw_anim": "822", @@ -45954,6 +45983,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "516900", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Dharok's greataxe", "tradeable": "true", @@ -45963,7 +45993,7 @@ { "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Dharok the Wretched's platebody.", + "examine": "Dharok the Wretched's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -45981,6 +46011,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "511400", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Dharok's platebody", "tradeable": "true", @@ -45990,7 +46021,7 @@ { "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Dharok the wretched's plate leg armour.", + "examine": "Dharok the Wretched's plate leg armour.", "durability": null, "low_alchemy": "110000", "high_alchemy": "165000", @@ -46007,6 +46038,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "748800", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Dharok's platelegs", "tradeable": "true", @@ -46017,7 +46049,7 @@ "remove_head": "true", "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Guthan the Infested's helm.", + "examine": "Guthan the Infested's helm", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -46035,6 +46067,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "396300", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Guthan's helm", "tradeable": "true", @@ -46044,7 +46077,7 @@ { "ge_buy_limit": "10", "turn90cw_anim": "1207", - "examine": "Guthan The Infested's Warspear.", + "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", "low_alchemy": "40000", "turn90ccw_anim": "1208", @@ -46073,6 +46106,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "588400", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Guthan's warspear", "tradeable": "true", @@ -46082,7 +46116,7 @@ { "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Guthan the Infested's platebody armour.", + "examine": "\tGuthan the Infested's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -46100,6 +46134,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "316800", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Guthan's platebody", "tradeable": "true", @@ -46126,6 +46161,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "315600", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Guthan's chainskirt", "tradeable": "true", @@ -46153,6 +46189,7 @@ { "ge_buy_limit": "1", "grand_exchange_price": "38900", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Karil's coif", "tradeable": "true", @@ -46192,6 +46229,7 @@ { "ge_buy_limit": "1", "grand_exchange_price": "188000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Karil's crossbow", "tradeable": "true", @@ -46201,7 +46239,7 @@ { "requirements": "{1,70}-{4,70}", "ge_buy_limit": "10000", - "examine": "Karil the Tainted's leatherbody.", + "examine": "Karil the Tainted's leather body armour.", "durability": null, "low_alchemy": "20000", "high_alchemy": "30000", @@ -46219,6 +46257,7 @@ { "ge_buy_limit": "10000", "grand_exchange_price": "1800000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Karil's leathertop", "tradeable": "true", @@ -46245,6 +46284,7 @@ { "ge_buy_limit": "10000", "grand_exchange_price": "217000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Karil's leatherskirt", "tradeable": "true", @@ -46289,6 +46329,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "139000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Torag's helm", "tradeable": "true", @@ -46320,6 +46361,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "111600", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Torag's hammers", "tradeable": "true", @@ -46329,7 +46371,7 @@ { "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Torag the Corrupted's plate body armour.", + "examine": "\tTorag the Corrupted's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -46347,6 +46389,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "346200", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Torag's platebody", "tradeable": "true", @@ -46373,6 +46416,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "427200", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Torag's platelegs", "tradeable": "true", @@ -46401,6 +46445,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "687200", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Verac's helm", "tradeable": "true", @@ -46410,7 +46455,7 @@ { "ge_buy_limit": "10", "turn90cw_anim": "2060", - "examine": "Verac the Defiled's Flail.", + "examine": "Verac the Defiled's flail.", "walk_anim": "1830", "low_alchemy": "64000", "turn90ccw_anim": "2060", @@ -46440,6 +46485,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "135500", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Verac's flail", "tradeable": "true", @@ -46467,6 +46513,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "270000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Verac's brassard", "tradeable": "true", @@ -46493,6 +46540,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "411400", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Verac's plateskirt", "tradeable": "true", @@ -47081,7 +47129,7 @@ { "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim The Blighted's Hood.", + "examine": "Ahrim the Blighted's leather hood.", "durability": null, "low_alchemy": "5200", "high_alchemy": "7800", @@ -47097,7 +47145,7 @@ { "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim The Blighted's Hood.", + "examine": "Ahrim the Blighted's leather hood.", "durability": null, "low_alchemy": "5200", "high_alchemy": "7800", @@ -47113,7 +47161,7 @@ { "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim The Blighted's Hood.", + "examine": "Ahrim the Blighted's leather hood.", "durability": null, "low_alchemy": "5200", "high_alchemy": "7800", @@ -47129,7 +47177,7 @@ { "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim The Blighted's Hood.", + "examine": "Ahrim the Blighted's leather hood.", "durability": null, "low_alchemy": "5200", "high_alchemy": "7800", @@ -47146,7 +47194,7 @@ "remove_head": "true", "requirements": "{1,70}-{6,70}", "ge_buy_limit": "10", - "examine": "Ahrim The Blighted's Hood.", + "examine": "Ahrim the Blighted's leather hood.", "durability": null, "low_alchemy": "5200", "high_alchemy": "7800", @@ -47162,6 +47210,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "51700", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Ahrim's hood 0", "tradeable": "true", @@ -47305,6 +47354,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "50600", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Ahrim's staff 0", "tradeable": "true", @@ -47395,6 +47445,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "1100000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Ahrim's top 0", "tradeable": "true", @@ -47480,6 +47531,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "1500000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Ahrim's skirt 0", "tradeable": "true", @@ -47489,7 +47541,7 @@ { "remove_head": "true", "requirements": "{1,70}", - "examine": "Dharok The Wretched's Helmet", + "examine": "Dharok the Wretched's helm.", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47505,7 +47557,7 @@ { "remove_head": "true", "requirements": "{1,70}", - "examine": "Dharok The Wretched's Helmet", + "examine": "Dharok the Wretched's helm.", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47521,7 +47573,7 @@ { "remove_head": "true", "requirements": "{1,70}", - "examine": "Dharok The Wretched's Helmet", + "examine": "Dharok the Wretched's helm.", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47537,7 +47589,7 @@ { "remove_head": "true", "requirements": "{1,70}", - "examine": "Dharok The Wretched's Helmet", + "examine": "Dharok the Wretched's helm.", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47554,7 +47606,7 @@ "remove_head": "true", "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Dharok The Wretched's Helmet", + "examine": "Dharok the Wretched's helm.", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47570,6 +47622,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "1300000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Dharok's helm 0", "tradeable": "true", @@ -47578,7 +47631,7 @@ }, { "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's Greataxe.", + "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", "low_alchemy": "83200", "turn90ccw_anim": "2064", @@ -47606,7 +47659,7 @@ }, { "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's Greataxe.", + "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", "low_alchemy": "83200", "turn90ccw_anim": "2064", @@ -47634,7 +47687,7 @@ }, { "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's Greataxe.", + "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", "low_alchemy": "83200", "turn90ccw_anim": "2064", @@ -47662,7 +47715,7 @@ }, { "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's Greataxe.", + "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", "low_alchemy": "83200", "turn90ccw_anim": "2064", @@ -47691,7 +47744,7 @@ { "ge_buy_limit": "10", "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's Greataxe.", + "examine": "Dharok the Wretched's greataxe.", "walk_anim": "2064", "low_alchemy": "83200", "turn90ccw_anim": "2064", @@ -47718,6 +47771,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "442600", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Dharok's axe 0", "tradeable": "true", @@ -47726,7 +47780,7 @@ }, { "requirements": "{1,70}", - "examine": "Dharok the Wretched's platebody.", + "examine": "Dharok the Wretched's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -47742,7 +47796,7 @@ }, { "requirements": "{1,70}", - "examine": "Dharok the Wretched's platebody.", + "examine": "Dharok the Wretched's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -47758,7 +47812,7 @@ }, { "requirements": "{1,70}", - "examine": "Dharok the Wretched's platebody.", + "examine": "Dharok the Wretched's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -47774,7 +47828,7 @@ }, { "requirements": "{1,70}", - "examine": "Dharok the Wretched's platebody.", + "examine": "Dharok the Wretched's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -47791,7 +47845,7 @@ { "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Dharok the Wretched's platebody.", + "examine": "Dharok the Wretched's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -47808,6 +47862,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "438200", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Dharok's body 0", "tradeable": "true", @@ -47816,7 +47871,7 @@ }, { "requirements": "{1,70}", - "examine": "Dharok the wretched's plate leg armour.", + "examine": "Dharok the Wretched's plate leg armour.", "durability": null, "low_alchemy": "110000", "high_alchemy": "165000", @@ -47831,7 +47886,7 @@ }, { "requirements": "{1,70}", - "examine": "Dharok the wretched's plate leg armour.", + "examine": "Dharok the Wretched's plate leg armour.", "durability": null, "low_alchemy": "110000", "high_alchemy": "165000", @@ -47846,7 +47901,7 @@ }, { "requirements": "{1,70}", - "examine": "Dharok the wretched's plate leg armour.", + "examine": "Dharok the Wretched's plate leg armour.", "durability": null, "low_alchemy": "110000", "high_alchemy": "165000", @@ -47861,7 +47916,7 @@ }, { "requirements": "{1,70}", - "examine": "Dharok the wretched's plate leg armour.", + "examine": "Dharok the Wretched's plate leg armour.", "durability": null, "low_alchemy": "110000", "high_alchemy": "165000", @@ -47877,7 +47932,7 @@ { "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Dharok the wretched's plate leg armour.", + "examine": "Dharok the Wretched's plate leg armour.", "durability": null, "low_alchemy": "110000", "high_alchemy": "165000", @@ -47893,6 +47948,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "676000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Dharok's legs 0", "tradeable": "true", @@ -47902,7 +47958,7 @@ { "remove_head": "true", "requirements": "{1,70}", - "examine": "Guthan the Infested's helm.", + "examine": "Guthan the Infested's helm", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47919,7 +47975,7 @@ { "remove_head": "true", "requirements": "{1,70}", - "examine": "Guthan the Infested's helm.", + "examine": "Guthan the Infested's helm", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47936,7 +47992,7 @@ { "remove_head": "true", "requirements": "{1,70}", - "examine": "Guthan the Infested's helm.", + "examine": "Guthan the Infested's helm", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47953,7 +48009,7 @@ { "remove_head": "true", "requirements": "{1,70}", - "examine": "Guthan the Infested's helm.", + "examine": "Guthan the Infested's helm", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47971,7 +48027,7 @@ "remove_head": "true", "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Guthan the Infested's helm.", + "examine": "Guthan the Infested's helm", "durability": null, "low_alchemy": "41200", "high_alchemy": "61800", @@ -47988,6 +48044,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "368200", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Guthan's helm 0", "tradeable": "true", @@ -47996,7 +48053,7 @@ }, { "turn90cw_anim": "1207", - "examine": "Guthan The Infested's Warspear.", + "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", "low_alchemy": "40000", "turn90ccw_anim": "1208", @@ -48024,7 +48081,7 @@ }, { "turn90cw_anim": "1207", - "examine": "Guthan The Infested's Warspear.", + "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", "low_alchemy": "40000", "turn90ccw_anim": "1208", @@ -48052,7 +48109,7 @@ }, { "turn90cw_anim": "1207", - "examine": "Guthan The Infested's Warspear.", + "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", "low_alchemy": "40000", "turn90ccw_anim": "1208", @@ -48080,7 +48137,7 @@ }, { "turn90cw_anim": "1207", - "examine": "Guthan The Infested's Warspear.", + "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", "low_alchemy": "40000", "turn90ccw_anim": "1208", @@ -48109,7 +48166,7 @@ { "ge_buy_limit": "10", "turn90cw_anim": "1207", - "examine": "Guthan The Infested's Warspear.", + "examine": "Guthan the Infested's warspear.", "walk_anim": "1205", "low_alchemy": "40000", "turn90ccw_anim": "1208", @@ -48136,6 +48193,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "379500", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Guthan's spear 0", "tradeable": "true", @@ -48144,7 +48202,7 @@ }, { "requirements": "{1,70}", - "examine": "Guthan the Infested's platebody armour.", + "examine": "\tGuthan the Infested's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48160,7 +48218,7 @@ }, { "requirements": "{1,70}", - "examine": "Guthan the Infested's platebody armour.", + "examine": "\tGuthan the Infested's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48176,7 +48234,7 @@ }, { "requirements": "{1,70}", - "examine": "Guthan the Infested's platebody armour.", + "examine": "\tGuthan the Infested's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48192,7 +48250,7 @@ }, { "requirements": "{1,70}", - "examine": "Guthan the Infested's platebody armour.", + "examine": "\tGuthan the Infested's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48209,7 +48267,7 @@ { "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Guthan the Infested's platebody armour.", + "examine": "\tGuthan the Infested's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48226,6 +48284,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "245900", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Guthan's body 0", "tradeable": "true", @@ -48311,6 +48370,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "240900", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Guthan's skirt 0", "tradeable": "true", @@ -48401,6 +48461,7 @@ { "ge_buy_limit": "1", "grand_exchange_price": "7557", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Karil's coif 0", "tradeable": "true", @@ -48548,6 +48609,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "106300", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Karil's x-bow 0", "tradeable": "true", @@ -48556,7 +48618,7 @@ }, { "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's leatherbody.", + "examine": "Karil the Tainted's leather body armour.", "durability": null, "low_alchemy": "20000", "high_alchemy": "30000", @@ -48572,7 +48634,7 @@ }, { "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's leatherbody.", + "examine": "Karil the Tainted's leather body armour.", "durability": null, "low_alchemy": "20000", "high_alchemy": "30000", @@ -48588,7 +48650,7 @@ }, { "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's leatherbody.", + "examine": "Karil the Tainted's leather body armour.", "durability": null, "low_alchemy": "20000", "high_alchemy": "30000", @@ -48604,7 +48666,7 @@ }, { "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's leatherbody.", + "examine": "Karil the Tainted's leather body armour.", "durability": null, "low_alchemy": "20000", "high_alchemy": "30000", @@ -48621,7 +48683,7 @@ { "requirements": "{1,70}-{4,70}", "ge_buy_limit": "1", - "examine": "Karil the Tainted's leatherbody.", + "examine": "Karil the Tainted's leather body armour.", "durability": null, "low_alchemy": "20000", "high_alchemy": "30000", @@ -48638,6 +48700,7 @@ { "ge_buy_limit": "1", "grand_exchange_price": "1500000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Karil's top 0", "tradeable": "true", @@ -48723,6 +48786,7 @@ { "ge_buy_limit": "1", "grand_exchange_price": "153100", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Karil's skirt 0", "tradeable": "true", @@ -48818,6 +48882,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "78200", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Torag's helm 0", "tradeable": "true", @@ -48926,6 +48991,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "95600", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Torag's hammer 0", "tradeable": "true", @@ -48934,7 +49000,7 @@ }, { "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate body armour.", + "examine": "\tTorag the Corrupted's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48950,7 +49016,7 @@ }, { "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate body armour.", + "examine": "\tTorag the Corrupted's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48966,7 +49032,7 @@ }, { "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate body armour.", + "examine": "\tTorag the Corrupted's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48982,7 +49048,7 @@ }, { "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate body armour.", + "examine": "\tTorag the Corrupted's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -48999,7 +49065,7 @@ { "requirements": "{1,70}", "ge_buy_limit": "10", - "examine": "Torag the Corrupted's plate body armour.", + "examine": "\tTorag the Corrupted's platebody armour.", "durability": null, "low_alchemy": "112000", "high_alchemy": "168000", @@ -49016,6 +49082,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "271600", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Torag's body 0", "tradeable": "true", @@ -49101,6 +49168,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "371300", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Torag's legs 0", "tradeable": "true", @@ -49196,6 +49264,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "648900", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Verac's helm 0", "tradeable": "true", @@ -49204,7 +49273,7 @@ }, { "turn90cw_anim": "2060", - "examine": "Verac the Defiled's Flail.", + "examine": "Verac the Defiled's flail.", "walk_anim": "1830", "low_alchemy": "64000", "turn90ccw_anim": "2060", @@ -49232,7 +49301,7 @@ }, { "turn90cw_anim": "2060", - "examine": "Verac the Defiled's Flail.", + "examine": "Verac the Defiled's flail.", "walk_anim": "1830", "low_alchemy": "64000", "turn90ccw_anim": "2060", @@ -49260,7 +49329,7 @@ }, { "turn90cw_anim": "2060", - "examine": "Verac the Defiled's Flail.", + "examine": "Verac the Defiled's flail.", "walk_anim": "1830", "low_alchemy": "64000", "turn90ccw_anim": "2060", @@ -49288,7 +49357,7 @@ }, { "turn90cw_anim": "2060", - "examine": "Verac the Defiled's Flail.", + "examine": "Verac the Defiled's flail.", "walk_anim": "1830", "low_alchemy": "64000", "turn90ccw_anim": "2060", @@ -49317,7 +49386,7 @@ { "ge_buy_limit": "10", "turn90cw_anim": "2060", - "examine": "Verac the Defiled's Flail.", + "examine": "Verac the Defiled's flail.", "walk_anim": "1830", "low_alchemy": "64000", "turn90ccw_anim": "2060", @@ -49344,6 +49413,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "95500", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Verac's flail 0", "tradeable": "true", @@ -49434,6 +49504,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "174000", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Verac's top 0", "tradeable": "true", @@ -49519,6 +49590,7 @@ { "ge_buy_limit": "10", "grand_exchange_price": "321600", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Verac's skirt 0", "tradeable": "true", @@ -68712,7 +68784,7 @@ { "shop_price": "54", "ge_buy_limit": "1000", - "examine": "All the good of the earth.", + "examine": "\tStill needs two more ingredients.", "grand_exchange_price": "77", "durability": null, "name": "Part mud pie", @@ -68725,6 +68797,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "77", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part mud pie", "tradeable": "true", @@ -68734,7 +68807,7 @@ { "shop_price": "54", "ge_buy_limit": "1000", - "examine": "All the good of the earth.", + "examine": "Still needs one more ingredient.", "grand_exchange_price": "284", "durability": null, "name": "Part mud pie", @@ -68747,6 +68820,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "284", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part mud pie", "tradeable": "true", @@ -68768,6 +68842,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1625", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Raw mud pie", "tradeable": "true", @@ -68800,6 +68875,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "2024", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Mud pie", "tradeable": "true", @@ -68808,7 +68884,7 @@ }, { "ge_buy_limit": "1000", - "examine": "What I wouldn't give for a good steak about now...", + "examine": "Still needs two more ingredients.", "grand_exchange_price": "94", "durability": null, "name": "Part garden pie", @@ -68821,6 +68897,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "94", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part garden pie", "tradeable": "true", @@ -68829,7 +68906,7 @@ }, { "ge_buy_limit": "1000", - "examine": "What I wouldn't give for a good steak about now...", + "examine": "Still needs one more ingredient.", "grand_exchange_price": "150", "durability": null, "name": "Part garden pie", @@ -68842,6 +68919,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "150", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part garden pie", "tradeable": "true", @@ -68864,6 +68942,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1052", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Raw garden pie", "tradeable": "true", @@ -68885,6 +68964,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1667", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Garden pie", "tradeable": "true", @@ -68906,6 +68986,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "442", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Half a garden pie", "tradeable": "true", @@ -68914,7 +68995,7 @@ }, { "ge_buy_limit": "1000", - "examine": "Bounty of the sea.", + "examine": "Still needs two more ingredients.", "grand_exchange_price": "51", "durability": null, "name": "Part fish pie", @@ -68927,6 +69008,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "51", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part fish pie", "tradeable": "true", @@ -68935,7 +69017,7 @@ }, { "ge_buy_limit": "1000", - "examine": "Bounty of the sea.", + "examine": "Still needs one more ingredient.", "grand_exchange_price": "691", "durability": null, "name": "Part fish pie", @@ -68948,6 +69030,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "691", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part fish pie", "tradeable": "true", @@ -68970,6 +69053,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1198", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Raw fish pie", "tradeable": "true", @@ -68991,6 +69075,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "121", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Fish pie", "tradeable": "true", @@ -69012,6 +69097,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "56", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Half a fish pie", "tradeable": "true", @@ -69020,7 +69106,7 @@ }, { "ge_buy_limit": "1000", - "examine": "Much tastier than a normal fish pie.", + "examine": "Still needs two more ingredients. ", "grand_exchange_price": "159", "durability": null, "name": "Part admiral pie", @@ -69031,6 +69117,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "159", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part admiral pie", "tradeable": "true", @@ -69039,7 +69126,7 @@ }, { "ge_buy_limit": "1000", - "examine": "Much tastier than a normal fish pie.", + "examine": "Still needs one more ingredient.", "grand_exchange_price": "537", "durability": null, "name": "Part admiral pie", @@ -69050,6 +69137,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "537", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part admiral pie", "tradeable": "true", @@ -69072,6 +69160,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1813", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Raw admiral pie", "tradeable": "true", @@ -69091,6 +69180,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "532", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Admiral pie", "tradeable": "true", @@ -69110,6 +69200,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "184", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Half an admiral pie", "tradeable": "true", @@ -69118,7 +69209,7 @@ }, { "ge_buy_limit": "1000", - "examine": "A triumph of man over nature.", + "examine": "Still needs two more ingredients.", "grand_exchange_price": "126", "durability": null, "name": "Part wild pie", @@ -69131,6 +69222,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "126", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part wild pie", "tradeable": "true", @@ -69139,7 +69231,7 @@ }, { "ge_buy_limit": "1000", - "examine": "A triumph of man over nature.", + "examine": "Still needs one more ingredient.", "grand_exchange_price": "1116", "durability": null, "name": "Part wild pie", @@ -69152,6 +69244,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1116", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part wild pie", "tradeable": "true", @@ -69173,6 +69266,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "3787", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Raw wild pie", "tradeable": "true", @@ -69194,6 +69288,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1322", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Wild pie", "tradeable": "true", @@ -69215,6 +69310,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "112", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Half a wild pie", "tradeable": "true", @@ -69223,7 +69319,7 @@ }, { "ge_buy_limit": "1000", - "examine": "All the fruits of a very small forest.", + "examine": "Still needs two more ingredients.", "grand_exchange_price": "495", "durability": null, "name": "Part summer pie", @@ -69236,6 +69332,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "495", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part summer pie", "tradeable": "true", @@ -69244,7 +69341,7 @@ }, { "ge_buy_limit": "1000", - "examine": "All the fruits of a very small forest.", + "examine": "Still needs one more ingredient.", "grand_exchange_price": "980", "durability": null, "name": "Part summer pie", @@ -69257,6 +69354,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "980", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Part summer pie", "tradeable": "true", @@ -69279,6 +69377,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "2577", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Raw summer pie", "tradeable": "true", @@ -69300,6 +69399,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "1288", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Summer pie", "tradeable": "true", @@ -69321,6 +69421,7 @@ { "ge_buy_limit": "1000", "grand_exchange_price": "430", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Half a summer pie", "tradeable": "true", @@ -123123,23 +123224,24 @@ "durability": null, "low_alchemy": "8", "high_alchemy": "12", - "destroy": "true", + "destroy": "false", "attack_speed": "4", "absorb": "0,0,0", "destroy_message": "Drop", "grand_exchange_price": "25", "name": "Crayfish cage", + "tradeable": "true", "archery_ticket_price": "0", "id": "13431", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { "shop_price": "20", - "examine": "Useful for catching crayfish.", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "low_alchemy": "8", "high_alchemy": "12", - "destroy": "true", + "destroy": "false", "attack_speed": "4", "destroy_message": "Drop", "grand_exchange_price": "25", @@ -123154,23 +123256,24 @@ "durability": null, "low_alchemy": "2", "high_alchemy": "3", - "destroy": "true", + "destroy": "false", "attack_speed": "4", "absorb": "0,0,0", "destroy_message": "Drop", "grand_exchange_price": "32", "name": "Crayfish", + "tradeable": "true", "archery_ticket_price": "0", "id": "13433", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { "shop_price": "5", - "examine": "Some cooked crayfish. Eat it to heal.", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "low_alchemy": "2", "high_alchemy": "3", - "destroy": "true", + "destroy": "false", "attack_speed": "4", "destroy_message": "Drop", "grand_exchange_price": "32", @@ -123185,23 +123288,24 @@ "durability": null, "low_alchemy": "2", "high_alchemy": "3", - "destroy": "true", + "destroy": "false", "attack_speed": "4", "absorb": "0,0,0", "destroy_message": "Drop", "grand_exchange_price": "41", "name": "Raw crayfish", + "tradeable": "true", "archery_ticket_price": "0", "id": "13435", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { "shop_price": "5", - "examine": "I should try cooking this. Used in Cooking (1).", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "low_alchemy": "2", "high_alchemy": "3", - "destroy": "true", + "destroy": "false", "attack_speed": "4", "destroy_message": "Drop", "grand_exchange_price": "41", @@ -123214,7 +123318,9 @@ "shop_price": "1", "durability": null, "name": "Burnt crayfish", + "tradeable": "true", "archery_ticket_price": "0", + "destroy": "false", "attack_speed": "4", "id": "13437", "absorb": "0,0,0", @@ -123222,10 +123328,12 @@ }, { "shop_price": "1", + "examine": "Swap this note at any bank for the equivalent item.", "durability": null, "name": "Burnt crayfish", "tradeable": "true", "archery_ticket_price": "0", + "destroy": "false", "attack_speed": "4", "id": "13438" }, From 2f6a65012fff10fe74d0cf0d51fa189211233f79 Mon Sep 17 00:00:00 2001 From: ceikry Date: Fri, 13 Aug 2021 07:49:57 -0500 Subject: [PATCH 02/12] Poisoning items without free inventory space should no longer nuke the product from existence --- .../game/interaction/item/withitem/PoisonWeaponPlugin.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Server/src/main/java/core/game/interaction/item/withitem/PoisonWeaponPlugin.java b/Server/src/main/java/core/game/interaction/item/withitem/PoisonWeaponPlugin.java index 09b3e0af6..03c8203ff 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/PoisonWeaponPlugin.java +++ b/Server/src/main/java/core/game/interaction/item/withitem/PoisonWeaponPlugin.java @@ -1,5 +1,6 @@ package core.game.interaction.item.withitem; +import api.ContentAPI; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.player.Player; @@ -7,6 +8,7 @@ import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Items; /** * Handles weapon poisiong. @@ -297,8 +299,8 @@ public class PoisonWeaponPlugin extends UseWithHandler { } int amt = Math.min(weaponItem.getAmount(), 5); player.getInventory().remove(new Item(weaponItem.getId(), amt)); - player.getInventory().add(new Item(229, 1)); - player.getInventory().add(new Item(product, amt)); + ContentAPI.addItemOrDrop(player, product, amt); + ContentAPI.addItemOrDrop(player, Items.VIAL_229, 1); player.getPacketDispatch().sendMessage("You poison the " + weaponItem.getName().toLowerCase() + "."); } From 18c052fe7c5e0e26ac3d6db81e8fed5c9c564ca8 Mon Sep 17 00:00:00 2001 From: Woahscam <7430825-woahscam@users.noreply.gitlab.com> Date: Fri, 13 Aug 2021 13:29:45 +0000 Subject: [PATCH 03/12] Changes to ExaminePacket.java --- .../java/core/net/packet/in/ExaminePacket.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Server/src/main/java/core/net/packet/in/ExaminePacket.java b/Server/src/main/java/core/net/packet/in/ExaminePacket.java index b37e310f9..47e916146 100644 --- a/Server/src/main/java/core/net/packet/in/ExaminePacket.java +++ b/Server/src/main/java/core/net/packet/in/ExaminePacket.java @@ -8,6 +8,7 @@ import core.cache.def.impl.VarbitDefinition; import core.game.node.entity.player.Player; import core.net.packet.IncomingPacket; import core.net.packet.IoBuffer; +import org.rs09.consts.Items; import java.util.Arrays; @@ -61,16 +62,27 @@ public final class ExaminePacket implements IncomingPacket { /** * Gets the item examine. - * @param id the id. - * @return the name. + * @param id the item id. + * @return the item examine. */ public static String getItemExamine(int id) { - if (id == 995) { + + // Coins examine override + if (id == Items.COINS_995) { return "Lovely money!"; } + + // Clue scroll examine override if (ItemDefinition.forId(id).getExamine().length() == 255) { return "A set of instructions to be followed."; } + + // Noted item examine override + if (!ItemDefinition.forId(id).isUnnoted()) { + return "Swap this note at any bank for the equivalent item."; + } + + // Return the examine string for the given item ID return ItemDefinition.forId(id).getExamine(); } } \ No newline at end of file From c072f9e1f9f9d245145c4e48d4a7c18c9ce439f6 Mon Sep 17 00:00:00 2001 From: Woahscam <7430825-woahscam@users.noreply.gitlab.com> Date: Fri, 13 Aug 2021 14:51:57 +0000 Subject: [PATCH 04/12] Turoth spawn and config fixes --- Server/data/configs/npc_configs.json | 71 ++++++++++++++++++++++++++-- Server/data/configs/npc_spawns.json | 16 +++---- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/Server/data/configs/npc_configs.json b/Server/data/configs/npc_configs.json index 9d818510c..400ae302f 100644 --- a/Server/data/configs/npc_configs.json +++ b/Server/data/configs/npc_configs.json @@ -16607,7 +16607,26 @@ "attack_level": "32" }, { - "examine": "It's one leg short!", + "examine": "He's one big leg short!", + "melee_animation": "9471", + "range_animation": "9471", + "attack_speed": "4", + "magic_level": "1", + "defence_animation": "9473", + "magic_animation": "9471", + "death_animation": "9472", + "name": "Turoth", + "defence_level": "85", + "safespot": null, + "lifepoints": "78", + "strength_level": "85", + "id": "1622", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "range_level": "1", + "attack_level": "55" + }, + { + "examine": "He's one big leg short!", "melee_animation": "9471", "range_animation": "9471", "attack_speed": "4", @@ -16618,9 +16637,10 @@ "name": "Turoth", "defence_level": "88", "safespot": null, - "lifepoints": "27", + "lifepoints": "81", "strength_level": "88", "id": "1623", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", "range_level": "1", "attack_level": "58" }, @@ -16663,7 +16683,7 @@ "attack_level": "105" }, { - "examine": "It's one leg short!", + "examine": "She's one leg short!", "melee_animation": "9471", "range_animation": "9471", "attack_speed": "4", @@ -16677,6 +16697,7 @@ "lifepoints": "79", "strength_level": "86", "id": "1626", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", "clue_level": "2", "range_level": "1", "attack_level": "56" @@ -16696,12 +16717,13 @@ "lifepoints": "77", "strength_level": "84", "id": "1627", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", "clue_level": "2", "range_level": "1", "attack_level": "54" }, { - "examine": "It's one leg short!", + "examine": "It's one small leg short!", "melee_animation": "9471", "range_animation": "9471", "attack_speed": "4", @@ -16715,10 +16737,51 @@ "lifepoints": "76", "strength_level": "83", "id": "1628", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", "clue_level": "2", "range_level": "1", "attack_level": "53" }, + { + "examine": "He's one big leg short!", + "melee_animation": "9471", + "range_animation": "9471", + "attack_speed": "4", + "magic_level": "1", + "defence_animation": "9473", + "magic_animation": "9471", + "death_animation": "9472", + "name": "Turoth", + "defence_level": "87", + "safespot": null, + "lifepoints": "80", + "strength_level": "87", + "id": "1629", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "range_level": "1", + "attack_level": "57" + }, + { + "examine": "He's one big leg short!", + "melee_animation": "9471", + "range_animation": "9471", + "attack_speed": "4", + "magic_level": "1", + "defence_animation": "9473", + "magic_animation": "9471", + "death_animation": "9472", + "name": "Turoth", + "defence_level": "87", + "safespot": null, + "lifepoints": "80", + "strength_level": "87", + "id": "1630", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "range_level": "1", + "attack_level": "57" + }, { "examine": "A rocky slug.", "slayer_task": "69", diff --git a/Server/data/configs/npc_spawns.json b/Server/data/configs/npc_spawns.json index ac5440270..043dc96e4 100644 --- a/Server/data/configs/npc_spawns.json +++ b/Server/data/configs/npc_spawns.json @@ -3547,29 +3547,29 @@ "npc_id": "1621", "loc_data": "{2797,10035,0,1,6}-{2787,10032,0,1,3}-{2802,10031,0,1,3}" }, + { + "npc_id": "1623", + "loc_data": "{2725,9999,0,1,6}" + }, { "npc_id": "1624", "loc_data": "{3179,5522,0,1,6}-{3223,9393,0,1,5}-{3225,9401,0,1,0}-{3222,9375,0,1,6}-{3220,9380,0,1,6}-{3215,9364,0,1,3}-{3213,9361,0,1,1}-{3212,9355,0,1,2}-{3230,9367,0,1,3}-{3240,9350,0,1,5}-{3248,9348,0,1,4}-{3255,9350,0,1,4}-{3256,9392,0,1,4}-{3168,5513,0,1,3}-{3174,5514,0,1,6}-{3178,5517,0,1,4}-{3178,5528,0,1,1}-{3169,5516,0,1,3}-{3179,5524,0,1,4}" }, { "npc_id": "1626", - "loc_data": "{3165,5537,0,1,3}-{3159,5538,0,1,4}-{3155,5539,0,1,3}-{3158,5537,0,1,4}" + "loc_data": "{2723,10005,0,1,0}-{2726,9998,0,1,1}-{2723,10009,0,1,3}" }, { "npc_id": "1627", - "loc_data": "{2724,10008,0,1,4}" + "loc_data": "{2724,10008,0,1,4}-{2721,10005,0,1,1}-{2724,10005,0,1,3}-{2729,10004,0,1,3}-{2724,10012,0,1,4}-{2718,10009,0,1,1}-{2721,10001,0,1,7}-{2720,9996,0,1,7}-{2721,10006,0,1,3}" }, { "npc_id": "1628", - "loc_data": "{2723,10004,0,1,4}-{2722,9999,0,1,1}-{2729,10002,0,1,1}" + "loc_data": "{2723,10004,0,1,4}-{2722,9999,0,1,1}-{2729,10002,0,1,1}-{3155,5536,0,1,4}-{3156,5540,0,1,3}-{3157,5538,0,1,2}-{3166,5537,0,1,1}-{3165,5541,0,1,3}" }, { "npc_id": "1629", - "loc_data": "{2723,10005,0,1,0}-{2726,9998,0,1,1}-{2723,10009,0,1,3}-{2727,9998,0,1,4}-{2727,10001,0,1,3}" - }, - { - "npc_id": "1630", - "loc_data": "{2721,10005,0,1,1}-{2724,10005,0,1,3}-{2729,10004,0,1,3}-{2724,10012,0,1,4}-{2718,10009,0,1,1}-{2721,10001,0,1,7}-{2720,9996,0,1,7}-{2721,10006,0,1,3}" + "loc_data": "{3162,5538,0,1,2}-{3157,5538,0,1,4}-{3155,5537,0,1,1}-{3164,5538,0,1,2}" }, { "npc_id": "1631", From 3135f14e7aad461fb3ae27d3c419e2917ce29967 Mon Sep 17 00:00:00 2001 From: ceikry Date: Fri, 13 Aug 2021 12:08:24 -0500 Subject: [PATCH 05/12] Fixed a bug with clipping assignment --- .../java/core/game/world/map/RegionManager.kt | 16 ++++++++-------- .../game/system/command/oldsys/VisualCommand.kt | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Server/src/main/java/core/game/world/map/RegionManager.kt b/Server/src/main/java/core/game/world/map/RegionManager.kt index 50f77a212..eaf6e062d 100644 --- a/Server/src/main/java/core/game/world/map/RegionManager.kt +++ b/Server/src/main/java/core/game/world/map/RegionManager.kt @@ -89,8 +89,8 @@ object RegionManager { if (!region.isHasFlags) { return -1 } - x -= x shr 6 shl 6 - y -= y shr 6 shl 6 + x -= (x shr 6) shl 6 + y -= (y shr 6) shl 6 return region.planes[z].flags.clippingFlags[x][y] } @@ -175,8 +175,8 @@ object RegionManager { if (!region.isHasFlags) { return } - x -= x shr 6 shl 6 - y -= y shr 6 shl 6 + x -= (x shr 6) shl 6 + y -= (y shr 6) shl 6 if (projectile) { region.planes[z].projectileFlags.flag(x, y, flag) } else { @@ -201,8 +201,8 @@ object RegionManager { if (!region.isHasFlags) { return } - x -= x shr 6 shl 6 - y -= y shr 6 shl 6 + x -= (x shr 6) shl 6 + y -= (y shr 6) shl 6 if (projectile) { region.planes[z].projectileFlags.unflag(x, y, flag) } else { @@ -226,8 +226,8 @@ object RegionManager { if (!region.isHasFlags) { return -1 } - x -= x shr 6 shl 6 - y -= y shr 6 shl 6 + x -= (x shr 6) shl 6 + y -= (y shr 6) shl 6 return region.planes[z].projectileFlags.clippingFlags[x][y] } diff --git a/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt b/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt index 7ee4bad1f..325521fcf 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt @@ -170,6 +170,10 @@ class VisualCommand : CommandPlugin() { } return true } + "teleallowed" -> { + player!!.debug("Is tele allowed here? " + RegionManager.isTeleportPermitted(player!!.location)) + return true + } "oib" -> player!!.interfaceManager.openInfoBars() "char" -> CharacterDesign.open(player) "savenpc" -> return true From 6f175869e751e904129516062b7d59373244341c Mon Sep 17 00:00:00 2001 From: ceikry Date: Fri, 13 Aug 2021 16:56:22 -0500 Subject: [PATCH 06/12] Fixed an exploit with prayer point decrementing --- .../core/game/node/entity/player/Player.java | 21 +++++ .../entity/player/link/prayer/DrainTask.java | 92 ------------------- .../entity/player/link/prayer/Prayer.java | 13 --- .../entity/player/link/prayer/PrayerType.java | 12 ++- 4 files changed, 28 insertions(+), 110 deletions(-) delete mode 100644 Server/src/main/java/core/game/node/entity/player/link/prayer/DrainTask.java diff --git a/Server/src/main/java/core/game/node/entity/player/Player.java b/Server/src/main/java/core/game/node/entity/player/Player.java index 93249b33c..09814cb0b 100644 --- a/Server/src/main/java/core/game/node/entity/player/Player.java +++ b/Server/src/main/java/core/game/node/entity/player/Player.java @@ -80,6 +80,7 @@ import rs09.game.ge.PlayerGrandExchange; import rs09.game.node.entity.combat.CombatSwingHandler; import rs09.game.node.entity.combat.equipment.EquipmentDegrader; import rs09.game.node.entity.skill.runecrafting.PouchManager; +import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; import rs09.game.node.entity.state.newsys.State; import rs09.game.node.entity.state.newsys.StateRepository; import rs09.game.world.GameWorld; @@ -354,6 +355,8 @@ public class Player extends Entity { private int archeryTotal = 0; + private int prayerActiveTicks = 0; + /** * Constructs a new {@code Player} {@code Object}. * @param details The player's details. @@ -497,6 +500,24 @@ public class Player extends Entity { details.getSession().disconnect(); getSession().setLastPing(Long.MAX_VALUE); } + //Decrements prayer points + if(!prayer.getActive().isEmpty() && prayerActiveTicks % 2 == 0){ + double amountDrain = 0; + for(PrayerType type : prayer.getActive()){ + double drain = type.getDrain(); + double bonus = (1/30f) * prayer.getPlayer().getProperties().getBonuses()[12]; + drain = drain * (1 + bonus); + drain = 0.6 / drain; + amountDrain += drain; + } + if(SkillcapePerks.isActive(SkillcapePerks.DIVINE_FAVOR,prayer.getPlayer()) && RandomFunction.random(100) <= 10){ + amountDrain = 0; + } + + getSkills().decrementPrayerPoints(amountDrain); + } else { + prayerActiveTicks = 0; + } } @Override diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/DrainTask.java b/Server/src/main/java/core/game/node/entity/player/link/prayer/DrainTask.java deleted file mode 100644 index 932769bae..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/DrainTask.java +++ /dev/null @@ -1,92 +0,0 @@ -package core.game.node.entity.player.link.prayer; - -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.system.task.NodeTask; -import core.game.world.map.zone.ZoneBorders; -import core.tools.RandomFunction; -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; - -/** - * Represents an event used to drain prayer points. - * @author 'Vexia - */ -public final class DrainTask extends NodeTask { - - /** - * Constructs a new {@code DrainTask} {@code Object}. - */ - public DrainTask() { - super(2); - } - - @Override - public boolean exec(Node node, Node... n) { - Player player = node.asPlayer(); - if (player.getPrayer().getActive().isEmpty()) { - getPulse().stop(); - return true; - } - player.getSkills().decrementPrayerPoints(getDrain(player.getPrayer())); - - if (player.getPrayer().getActive().contains(PrayerType.PIETY) - && new ZoneBorders(2732, 3467, 2739, 3471, 0).insideBorder(player)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 3); - } - - return player.getSkills().getPrayerPoints() <= 0; - } - - @Override - public void stop(Node node, Node... n) { - final Player player = node.asPlayer(); - if (player.getSkills().getPrayerPoints() <= 0) { - player.getPrayer().reset(); - player.getAudioManager().send(2672); - player.getPacketDispatch().sendMessage("You have run out of prayer points, you must recharge at an altar."); - } - super.stop(node, n); - } - - @Override - public void start(Node node, Node... n) { - node.asPlayer().removeAttribute("prayer-message"); - super.start(node, n); - } - - @Override - public boolean removeFor(String s, Node node, Node... n) { - return true; - } - - /** - * Method used to initialize this pulse. - * @param player the player. - */ - public void init(final Player player) { - if (getPulse() == null || !getPulse().isRunning()) { - schedule(player); - } - } - - /** - * Method used to return that drain tick. - * @param prayer the prayer manager. - * @return the drain tick, converted to an integer. - */ - public double getDrain(final Prayer prayer) { - double amountDrain = 0; - for (PrayerType type : prayer.getActive()) { - double drain = type.getDrain(); - double bonus = (1/30f) * prayer.getPlayer().getProperties().getBonuses()[12]; - drain = drain * (1 + bonus); - drain = 0.6 / drain; - amountDrain += drain; - } - if(SkillcapePerks.isActive(SkillcapePerks.DIVINE_FAVOR,prayer.getPlayer()) && RandomFunction.random(100) <= 10){ - amountDrain = 0; - } - return amountDrain; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java index e71e7914f..81474255a 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java +++ b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java @@ -28,11 +28,6 @@ public final class Prayer { */ private final List active = new ArrayList<>(20); - /** - * Represents the current draining task. - */ - private final DrainTask task = new DrainTask(); - /** * Represents the player instance. */ @@ -159,14 +154,6 @@ public final class Prayer { return player; } - /** - * Gets the task. - * @return The task. - */ - public DrainTask getTask() { - return task; - } - /** * Gets the active prayers. * @return The active. diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/PrayerType.java b/Server/src/main/java/core/game/node/entity/player/link/prayer/PrayerType.java index f21292e92..d3496426a 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/PrayerType.java +++ b/Server/src/main/java/core/game/node/entity/player/link/prayer/PrayerType.java @@ -1,10 +1,12 @@ package core.game.node.entity.player.link.prayer; +import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.SkillRestoration; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.audio.Audio; +import core.game.world.map.zone.ZoneBorders; import core.tools.StringUtils; import java.util.List; @@ -251,16 +253,16 @@ public enum PrayerType { if (on) { flag(player, this); player.getPrayer().getActive().add(this); - player.getPrayer().getTask().init(player); iconify(player, getIcon(player, this)); player.getAudioManager().send(getSound()); + + if (this == PrayerType.PIETY + && new ZoneBorders(2732, 3467, 2739, 3471, 0).insideBorder(player)) { + player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 3); + } } else { player.getPrayer().getActive().remove(this); findNextIcon(player); - if (player.getPrayer().getActive().isEmpty()) { - player.getPrayer().getTask().stop(player); - return true; - } } return true; } From 54c2ae11f6f3e51fb1266f83d425ee7640af2c58 Mon Sep 17 00:00:00 2001 From: ceikry Date: Fri, 13 Aug 2021 17:00:36 -0500 Subject: [PATCH 07/12] Refactor prayer changes into a more organized location --- .../core/game/node/entity/player/Player.java | 21 ++--------------- .../entity/player/link/prayer/Prayer.java | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Server/src/main/java/core/game/node/entity/player/Player.java b/Server/src/main/java/core/game/node/entity/player/Player.java index 09814cb0b..e119932bc 100644 --- a/Server/src/main/java/core/game/node/entity/player/Player.java +++ b/Server/src/main/java/core/game/node/entity/player/Player.java @@ -355,8 +355,6 @@ public class Player extends Entity { private int archeryTotal = 0; - private int prayerActiveTicks = 0; - /** * Constructs a new {@code Player} {@code Object}. * @param details The player's details. @@ -500,24 +498,9 @@ public class Player extends Entity { details.getSession().disconnect(); getSession().setLastPing(Long.MAX_VALUE); } - //Decrements prayer points - if(!prayer.getActive().isEmpty() && prayerActiveTicks % 2 == 0){ - double amountDrain = 0; - for(PrayerType type : prayer.getActive()){ - double drain = type.getDrain(); - double bonus = (1/30f) * prayer.getPlayer().getProperties().getBonuses()[12]; - drain = drain * (1 + bonus); - drain = 0.6 / drain; - amountDrain += drain; - } - if(SkillcapePerks.isActive(SkillcapePerks.DIVINE_FAVOR,prayer.getPlayer()) && RandomFunction.random(100) <= 10){ - amountDrain = 0; - } - getSkills().decrementPrayerPoints(amountDrain); - } else { - prayerActiveTicks = 0; - } + //Decrements prayer points + getPrayer().tick(); } @Override diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java index 81474255a..1db718adb 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java +++ b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java @@ -12,6 +12,7 @@ import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; +import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; import java.util.ArrayList; import java.util.List; @@ -33,6 +34,8 @@ public final class Prayer { */ private final Player player; + private int prayerActiveTicks = 0; + /** * Constructs a new {@code Prayer} {@code Object}. */ @@ -110,6 +113,26 @@ public final class Prayer { } } + public void tick() { + if(!getActive().isEmpty() && prayerActiveTicks % 2 == 0){ + double amountDrain = 0; + for(PrayerType type : getActive()){ + double drain = type.getDrain(); + double bonus = (1/30f) * getPlayer().getProperties().getBonuses()[12]; + drain = drain * (1 + bonus); + drain = 0.6 / drain; + amountDrain += drain; + } + if(SkillcapePerks.isActive(SkillcapePerks.DIVINE_FAVOR, getPlayer()) && RandomFunction.random(100) <= 10){ + amountDrain = 0; + } + + getPlayer().getSkills().decrementPrayerPoints(amountDrain); + } else { + prayerActiveTicks = 0; + } + } + /** * Gets the skill bonus for the given skill id. * @param skillId The skill id. From a27008fd378088d11f2eb860e449d370e02b175b Mon Sep 17 00:00:00 2001 From: ceikry Date: Fri, 13 Aug 2021 17:03:53 -0500 Subject: [PATCH 08/12] Wrap up prayer changes --- .../java/core/game/node/entity/player/link/prayer/Prayer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java index 1db718adb..b8eb6e35f 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java +++ b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java @@ -128,6 +128,7 @@ public final class Prayer { } getPlayer().getSkills().decrementPrayerPoints(amountDrain); + prayerActiveTicks++; } else { prayerActiveTicks = 0; } From aa1e6f9ea0b62b53359db6b916c8aac88f703088 Mon Sep 17 00:00:00 2001 From: ceikry Date: Fri, 13 Aug 2021 17:08:33 -0500 Subject: [PATCH 09/12] finalized prayer changes --- .../core/game/node/entity/player/link/prayer/Prayer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java index b8eb6e35f..3d354ecf0 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java +++ b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java @@ -114,7 +114,10 @@ public final class Prayer { } public void tick() { - if(!getActive().isEmpty() && prayerActiveTicks % 2 == 0){ + if(!getActive().isEmpty()) prayerActiveTicks ++; + else prayerActiveTicks = 0; + + if(prayerActiveTicks > 0 && prayerActiveTicks % 2 == 0){ double amountDrain = 0; for(PrayerType type : getActive()){ double drain = type.getDrain(); @@ -128,9 +131,6 @@ public final class Prayer { } getPlayer().getSkills().decrementPrayerPoints(amountDrain); - prayerActiveTicks++; - } else { - prayerActiveTicks = 0; } } From 0e2f51f851b310618d19168abb3d52229a60fae9 Mon Sep 17 00:00:00 2001 From: ceikry Date: Fri, 13 Aug 2021 17:24:36 -0500 Subject: [PATCH 10/12] Clear + disable prayers (more cleanup) --- .../java/core/game/node/entity/player/link/prayer/Prayer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java index 3d354ecf0..200f24431 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java +++ b/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java @@ -131,6 +131,9 @@ public final class Prayer { } getPlayer().getSkills().decrementPrayerPoints(amountDrain); + if(getPlayer().getSkills().getPrayerPoints() == 0){ + reset(); + } } } From b23aa7bc0d1e6ee30fe82ff9ebe1cee112216961 Mon Sep 17 00:00:00 2001 From: Ceikry Date: Mon, 16 Aug 2021 02:22:10 +0000 Subject: [PATCH 11/12] Nature Spirit quest --- Server/data/configs/ground_spawns.json | 8 + Server/data/configs/npc_configs.json | 242 +----------------- Server/data/configs/npc_spawns.json | 12 +- Server/data/configs/shops.json | 9 + .../game/content/consumable/Consumable.java | 4 + .../game/content/dialogue/DrezelDialogue.java | 2 +- .../dialogue/DrezelMonumentDialogue.java | 53 +++- .../content/dialogue/RugMerchantDialogue.java | 2 +- .../game/interaction/item/CasketPlugin.java | 19 +- .../interaction/item/SilverSicklePlugin.java | 36 +-- .../object/PriestInPerilOptionPlugin.java | 3 +- .../java/core/game/node/entity/Entity.java | 1 - .../game/node/entity/combat/DeathTask.java | 3 +- .../game/node/entity/impl/Properties.java | 3 + .../java/core/game/node/entity/npc/NPC.java | 4 + .../core/game/node/entity/player/Player.java | 3 +- .../entity/player/link/PacketDispatch.java | 5 +- .../link/request/trade/TradeContainer.java | 2 +- .../decoration/chapel/BoneOfferPlugin.java | 7 +- .../entity/skill/herblore/FinishedPotion.java | 3 +- .../java/core/game/world/map/Location.java | 22 +- .../context/PositionedGraphicContext.java | 9 +- .../core/net/packet/in/InteractionPacket.java | 2 +- .../net/packet/out/PositionedGraphic.java | 15 +- Server/src/main/kotlin/api/ContentAPI.kt | 23 ++ .../naturespirit/FillimanTarlockNPC.kt | 32 +++ .../members/naturespirit/NSDrezelDialogue.kt | 120 +++++++++ .../quest/members/naturespirit/NSListeners.kt | 234 +++++++++++++++++ .../members/naturespirit/NSTarlockDialogue.kt | 241 +++++++++++++++++ .../quest/members/naturespirit/NSUtils.kt | 125 +++++++++ .../naturespirit/NatureSpiritDialogue.kt | 164 ++++++++++++ .../members/naturespirit/NatureSpiritQuest.kt | 122 +++++++++ .../game/interaction/InteractionListener.kt | 1 + .../game/interaction/InteractionListeners.kt | 1 + .../interaction/region/MorytaniaListeners.kt | 6 +- .../node/entity/npc/other/MortMyreGhastNPC.kt | 104 ++++++++ .../game/system/config/NPCConfigParser.kt | 1 + .../StringTools.kt => Globals.kt} | 0 38 files changed, 1345 insertions(+), 298 deletions(-) create mode 100644 Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/FillimanTarlockNPC.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSDrezelDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSListeners.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSTarlockDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSUtils.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritQuest.kt create mode 100644 Server/src/main/kotlin/rs09/game/node/entity/npc/other/MortMyreGhastNPC.kt rename Server/src/main/kotlin/rs09/tools/{stringtools/StringTools.kt => Globals.kt} (100%) diff --git a/Server/data/configs/ground_spawns.json b/Server/data/configs/ground_spawns.json index 92c2281d0..a08750389 100644 --- a/Server/data/configs/ground_spawns.json +++ b/Server/data/configs/ground_spawns.json @@ -530,5 +530,13 @@ { "item_id": "4619", "loc_data": "{1,2713,4913,0,2}" + }, + { + "item_id": "2964", + "loc_data": "{1,3437,3337,0,100}" + }, + { + "item_id": "2957", + "loc_data": "{1,3443,9742,0,100}-{1,3443,9742,1,100}" } ] diff --git a/Server/data/configs/npc_configs.json b/Server/data/configs/npc_configs.json index 400ae302f..e6270d816 100644 --- a/Server/data/configs/npc_configs.json +++ b/Server/data/configs/npc_configs.json @@ -10595,28 +10595,31 @@ "lifepoints": "10", "strength_level": "1", "id": "1052", - "aggressive": "true", + "aggressive": "false", "range_level": "1", "attack_level": "1" }, { "examine": "Arrghhh... A Ghast.", "range_animation": "0", - "attack_speed": "5", + "melee_animation": "1087", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "1088", "weakness": "9", "magic_animation": "0", - "death_animation": "9467", + "death_animation": "1089", + "death_gfx": "265", "name": "Ghast", - "defence_level": "25", + "defence_level": "18", "safespot": null, - "lifepoints": "71", - "strength_level": "25", + "lifepoints": "45", + "strength_level": "22", "id": "1053", "aggressive": "true", "range_level": "1", - "attack_level": "25" + "attack_level": "22" }, { "examine": "Ticket trader for the Brimhaven Agility Arena.", @@ -69050,22 +69053,6 @@ "range_level": "1", "attack_level": "55" }, - { - "examine": "The oldest man in Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghaslor the Elder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3029", - "range_level": "1", - "attack_level": "1" - }, { "examine": "A water salesman from Pollnivneach.", "melee_animation": "0", @@ -84891,43 +84878,6 @@ "projectile": "100", "attack_level": "25" }, - { - "examine": "Arrghhh... A Ghast.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghast", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1052", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Arrghhh... A Ghast.", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "9467", - "name": "Ghast", - "defence_level": "25", - "safespot": null, - "lifepoints": "71", - "strength_level": "25", - "id": "1053", - "aggressive": "true", - "range_level": "1", - "attack_level": "25" - }, { "examine": "Ticket trader for the Brimhaven Agility Arena.", "melee_animation": "0", @@ -102186,22 +102136,6 @@ "range_level": "1", "attack_level": "55" }, - { - "examine": "The oldest man in Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghaslor the Elder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3029", - "range_level": "1", - "attack_level": "1" - }, { "examine": "A water salesman from Pollnivneach.", "melee_animation": "0", @@ -143465,22 +143399,6 @@ "range_level": "1", "attack_level": "55" }, - { - "examine": "The oldest man in Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghaslor the Elder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3029", - "range_level": "1", - "attack_level": "1" - }, { "examine": "A water salesman from Pollnivneach.", "melee_animation": "0", @@ -159314,43 +159232,6 @@ "projectile": "100", "attack_level": "25" }, - { - "examine": "Arrghhh... A Ghast.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghast", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1052", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Arrghhh... A Ghast.", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "9467", - "name": "Ghast", - "defence_level": "25", - "safespot": null, - "lifepoints": "71", - "strength_level": "25", - "id": "1053", - "aggressive": "true", - "range_level": "1", - "attack_level": "25" - }, { "examine": "Ticket trader for the Brimhaven Agility Arena.", "melee_animation": "0", @@ -176609,22 +176490,6 @@ "range_level": "1", "attack_level": "55" }, - { - "examine": "The oldest man in Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghaslor the Elder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3029", - "range_level": "1", - "attack_level": "1" - }, { "examine": "A water salesman from Pollnivneach.", "melee_animation": "0", @@ -217640,22 +217505,6 @@ "range_level": "1", "attack_level": "55" }, - { - "examine": "The oldest man in Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghaslor the Elder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3029", - "range_level": "1", - "attack_level": "1" - }, { "examine": "A water salesman from Pollnivneach.", "melee_animation": "0", @@ -233481,43 +233330,6 @@ "projectile": "100", "attack_level": "25" }, - { - "examine": "Arrghhh... A Ghast.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghast", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1052", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Arrghhh... A Ghast.", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "9467", - "name": "Ghast", - "defence_level": "25", - "safespot": null, - "lifepoints": "71", - "strength_level": "25", - "id": "1053", - "aggressive": "true", - "range_level": "1", - "attack_level": "25" - }, { "examine": "Ticket trader for the Brimhaven Agility Arena.", "melee_animation": "0", @@ -250776,22 +250588,6 @@ "range_level": "1", "attack_level": "55" }, - { - "examine": "The oldest man in Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghaslor the Elder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3029", - "range_level": "1", - "attack_level": "1" - }, { "examine": "A water salesman from Pollnivneach.", "melee_animation": "0", @@ -292055,22 +291851,6 @@ "range_level": "1", "attack_level": "55" }, - { - "examine": "The oldest man in Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghaslor the Elder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3029", - "range_level": "1", - "attack_level": "1" - }, { "examine": "A water salesman from Pollnivneach.", "melee_animation": "0", diff --git a/Server/data/configs/npc_spawns.json b/Server/data/configs/npc_spawns.json index 043dc96e4..41e8744dc 100644 --- a/Server/data/configs/npc_spawns.json +++ b/Server/data/configs/npc_spawns.json @@ -2484,7 +2484,7 @@ "loc_data": "{3409,3489,0,1,4}-{3413,3485,0,1,4}-{3414,3491,1,1,6}" }, { - "npc_id": "1047", + "npc_id": "7690", "loc_data": "{3415,3489,2,0,0}" }, { @@ -2492,7 +2492,7 @@ "loc_data": "{3437,3486,0,0,0}" }, { - "npc_id": "1049", + "npc_id": "7707", "loc_data": "{3440,9895,0,0,0}" }, { @@ -10734,5 +10734,13 @@ { "npc_id": "1164", "loc_data": "{2780,3058,0,1,0}" + }, + { + "npc_id": "1051", + "loc_data": "{3440,9738,1,1,0}" + }, + { + "npc_id": "1052", + "loc_data": "{3420,3442,0,1,0}-{3429,3436,0,1,0}-{3439,3437,0,1,0}-{3448,3444,0,1,0}-{3458,3433,0,1,0}-{3459,3419,0,1,0}-{3449,3420,0,1,0}-{3441,3421,0,1,0}-{3427,3421,0,1,0}-{3414,3422,0,1,0}-{3411,3423,0,1,0}-{3415,3414,0,1,0}-{3410,3409,0,1,0}-{3422,3405,0,1,0}-{3431,3402,0,1,0}-{3444,3405,0,1,0}-{3453,3397,0,1,0}-{3467,3402,0,1,0}-{3473,3395,0,1,0}-{3470,3385,0,1,0}-{3463,3379,0,1,0}-{3451,3380,0,1,0}-{3445,3374,0,1,0}-{3438,3365,0,1,0}-{3429,3367,0,1,0}-{3417,3367,0,1,0}-{3413,3362,0,1,0}-{3417,3358,0,1,0}-{3426,3352,0,1,0}-{3435,3349,0,1,0}-{3442,3353,0,1,0}-{3448,3358,0,1,0}-{3457,3355,0,1,0}-{3461,3348,0,1,0}-{3457,3343,0,1,0}-{3471,3344,0,1,0}-{3426,3338,0,1,0}-{3423,3335,0,1,0}" } ] \ No newline at end of file diff --git a/Server/data/configs/shops.json b/Server/data/configs/shops.json index 1373c4579..2dfe0ffa0 100644 --- a/Server/data/configs/shops.json +++ b/Server/data/configs/shops.json @@ -2131,5 +2131,14 @@ "id": "240", "title": "Tiadeche's Karambwan Stall", "stock": "{3142,10}-{3157,10}" + }, + { + "npcs": "", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "241", + "title": "Wishing Well", + "stock": "{12204,10}-{12207,10}-{12210,0}-{12213,0}-{12216,0}-{12219,0}-{12222,0}-{12183,65000}-{12155,5000}" } ] \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/consumable/Consumable.java b/Server/src/main/java/core/game/content/consumable/Consumable.java index 4936fa978..789099637 100644 --- a/Server/src/main/java/core/game/content/consumable/Consumable.java +++ b/Server/src/main/java/core/game/content/consumable/Consumable.java @@ -109,6 +109,10 @@ public abstract class Consumable implements Plugin { return effect.getHealthEffectValue(player); } + public ConsumableEffect getEffect() { + return effect; + } + public int[] getIds() { return ids; } diff --git a/Server/src/main/java/core/game/content/dialogue/DrezelDialogue.java b/Server/src/main/java/core/game/content/dialogue/DrezelDialogue.java index 65adacc77..3dd359c6c 100644 --- a/Server/src/main/java/core/game/content/dialogue/DrezelDialogue.java +++ b/Server/src/main/java/core/game/content/dialogue/DrezelDialogue.java @@ -381,6 +381,6 @@ public final class DrezelDialogue extends DialoguePlugin { @Override public int[] getIds() { - return new int[] { 1047 }; + return new int[] { 7690 }; } } diff --git a/Server/src/main/java/core/game/content/dialogue/DrezelMonumentDialogue.java b/Server/src/main/java/core/game/content/dialogue/DrezelMonumentDialogue.java index 1af228462..6d4f62163 100644 --- a/Server/src/main/java/core/game/content/dialogue/DrezelMonumentDialogue.java +++ b/Server/src/main/java/core/game/content/dialogue/DrezelMonumentDialogue.java @@ -1,10 +1,16 @@ package core.game.content.dialogue; +import api.ContentAPI; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import org.rs09.consts.NPCs; +import rs09.game.content.quest.members.naturespirit.NSDrezelDialogue; +import rs09.game.system.SystemLogger; + +import static rs09.tools.DialogueConstKt.END_DIALOGUE; /** * Represents the dialogue plugin used for the drezel monument. @@ -44,7 +50,7 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); + Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); if (quest.getStage(player) == 17) { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah, " + player.getUsername() + ". I see you finally made it down here.", "Things are worse than I feared. I'm not sure if I will", "be able to repair the damage."); stage = 900; @@ -64,9 +70,23 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So can I pass through that barrier now?"); stage = 400; return true; - } else { + } + + /*else { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings again adventurer, How go your travels in", "Morytania? Is it as evil as I have heard?"); stage = 420; + }*/ + + quest = player.getQuestRepository().getQuest("Nature Spirit"); + + if(quest.getStage(player) <= 5){ + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings again adventurer, How go your travels in", "Morytania? Is it as evil as I have heard?"); + stage = 420; + } else if (quest.getStage(player) < 100){ + ContentAPI.openDialogue(player, new NSDrezelDialogue(), npc); + } else { + npcl(FacialExpression.NEUTRAL, "I heard you finished your quest with Filliman! Great work!"); + stage = END_DIALOGUE; } return true; } @@ -108,16 +128,37 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { end(); break; case 420: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, I'm going to look around a bit more."); + options("Well, I'm going to look around a bit more.", "Is there anything else interesting to do around here?"); stage = 421; break; case 421: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, that sounds like a good idea. Don't get into any", "trouble though!"); - stage = 422; + switch(buttonId){ + case 1: + playerl(FacialExpression.FRIENDLY, "Well, I'm going to look around a bit more."); + stage++; + break; + case 2: + playerl(FacialExpression.HALF_THINKING, "Is there anything else interesting to do around here?"); + stage = 425; + break; + } break; case 422: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, that sounds like a good idea. Don't get into any", "trouble though!"); + stage = 423; + break; + case 423: end(); break; + case 425: + npcl(FacialExpression.HALF_THINKING, "Well, not a great deal... but there is something you can do for me if you're interested. Though it is quite dangerous."); + stage++; + break; + case 426: + end(); + player.getDialogueInterpreter().open(new NSDrezelDialogue(), npc); + player.getDialogueInterpreter().handle(0,0); + break; case 120: interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I need " + player.getGameAttributes().getAttribute("priest-in-peril:rune", 50) + " more."); stage = 121; @@ -239,6 +280,6 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { @Override public int[] getIds() { - return new int[] { 1049 }; + return new int[] { NPCs.DREZEL_7707 }; } } diff --git a/Server/src/main/java/core/game/content/dialogue/RugMerchantDialogue.java b/Server/src/main/java/core/game/content/dialogue/RugMerchantDialogue.java index 97fa3263f..23ce43b07 100644 --- a/Server/src/main/java/core/game/content/dialogue/RugMerchantDialogue.java +++ b/Server/src/main/java/core/game/content/dialogue/RugMerchantDialogue.java @@ -20,7 +20,7 @@ import core.plugin.Plugin; import rs09.plugin.PluginManager; import org.rs09.consts.Items; -import static rs09.tools.stringtools.StringToolsKt.colorize; +import static rs09.tools.stringtools.GlobalsKt.colorize; /** diff --git a/Server/src/main/java/core/game/interaction/item/CasketPlugin.java b/Server/src/main/java/core/game/interaction/item/CasketPlugin.java index 9291f5e2d..2c8f9cbf8 100644 --- a/Server/src/main/java/core/game/interaction/item/CasketPlugin.java +++ b/Server/src/main/java/core/game/interaction/item/CasketPlugin.java @@ -3,6 +3,7 @@ package core.game.interaction.item; import java.util.ArrayList; import java.util.List; +import api.ContentAPI; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -13,6 +14,9 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; import core.tools.StringUtils; +import org.rs09.consts.Items; +import rs09.game.content.global.WeightBasedTable; +import rs09.game.content.global.WeightedItem; /** * Represents the casket handling plugin. @@ -25,7 +29,16 @@ public final class CasketPlugin extends OptionHandler { /** * Represents the casket rewards. */ - private static final ChanceItem[] CASKET_REWARD = new ChanceItem[] { new ChanceItem(995, 8, 3000, 30), new ChanceItem(1623, 1, 30), new ChanceItem(1621, 1, 70), new ChanceItem(1619, 1, 70), new ChanceItem(1617, 1, 97), new ChanceItem(987, 1, 97), new ChanceItem(985, 1, 97), new ChanceItem(1454, 1, 30), new ChanceItem(1452, 1, 70), new ChanceItem(1462, 1, 97) }; + private WeightBasedTable table = WeightBasedTable.create( + new WeightedItem(Items.COINS_995, 20, 640, 55, false), + new WeightedItem(Items.UNCUT_SAPPHIRE_1623, 1, 1, 32, false), + new WeightedItem(Items.UNCUT_EMERALD_1621, 1, 1, 16, false), + new WeightedItem(Items.UNCUT_RUBY_1619, 1, 1, 9, false), + new WeightedItem(Items.UNCUT_DIAMOND_1617, 1, 1, 2, false), + new WeightedItem(Items.COSMIC_TALISMAN_1454, 1, 1, 8, false), + new WeightedItem(Items.LOOP_HALF_OF_A_KEY_987, 1, 1, 1, false), + new WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985, 1, 1, 1, false) + ); @Override public Plugin newInstance(Object arg) throws Throwable { @@ -35,10 +48,10 @@ public final class CasketPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final ChanceItem reward = getChanceItem(CASKET_REWARD); + final Item reward = table.roll().get(0); player.getInventory().remove((Item) node); player.getDialogueInterpreter().sendItemMessage(reward, "You open the casket. Inside you find " + (reward.getAmount() > 1 ? "some" : (StringUtils.isPlusN(reward.getName()) ? "an" : "a")) + " " + reward.getName().toLowerCase() + "."); - player.getInventory().add(reward.getAmount() == 1 ? reward : new Item(reward.getId(), RandomFunction.random(reward.getMinimumAmount(), reward.getMaximumAmount()))); + ContentAPI.addItemOrDrop(player, reward.getId(), reward.getAmount()); return true; } diff --git a/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java b/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java index 8e3b1a41d..3c0713edd 100644 --- a/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java +++ b/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java @@ -12,6 +12,7 @@ import core.game.world.map.RegionManager; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import rs09.game.content.quest.members.naturespirit.NSUtils; /** * Handles the Silver Sickle (b) to collect Mort Myre Fungus. @@ -29,45 +30,14 @@ public final class SilverSicklePlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - Region region = RegionManager.forId(player.getLocation().getRegionId()); switch (option) { case "operate": case "cast bloom": - if (player.getSkills().getPrayerPoints() < 1) { - player.getPacketDispatch().sendMessage("You don't have enough prayer points to do this."); - } - for (Scenery[] o : region.getPlanes()[0].getObjects()) { - for (Scenery obj : o) { - if (obj != null) { - if (obj.getName().equalsIgnoreCase("Rotting log") && player.getSkills().getPrayerPoints() >= 1) { - if (player.getLocation().withinDistance(obj.getLocation(), 2)) { - handleVisuals(player, node); - SceneryBuilder.add(new Scenery(3509, obj.getLocation(), obj.getRotation())); - } - } - } - } - } - RegionManager.getLock().unlock(); + player.getPacketDispatch().sendAnimation(9021); + NSUtils.castBloom(player); return true; } return false; } - /** - * Handles the draining of prayer points and physical graphics and - * animation. - */ - public void handleVisuals(Player player, Node node) { - player.getSkills().decrementPrayerPoints(RandomFunction.random(1, 3)); - player.getPacketDispatch().sendAnimation(9021); - final Location[] AROUND_YOU = new Location[] { Location.create(player.getLocation().getX() - 1, player.getLocation().getY(), 0), Location.create(player.getLocation().getX() + 1, player.getLocation().getY(), 0), Location.create(player.getLocation().getX(), player.getLocation().getY() - 1, 0), Location.create(player.getLocation().getX(), player.getLocation().getY() + 1, 0), Location.create(player.getLocation().getX() + 1, player.getLocation().getY() + 1, 0), Location.create(player.getLocation().getX() - 1, player.getLocation().getY() + 1, 0), Location.create(player.getLocation().getX() + 1, player.getLocation().getY() - 1, 0), Location.create(player.getLocation().getX() - 1, player.getLocation().getY() - 1, 0), Location.create(player.getLocation().getX() + 1, player.getLocation().getY() + 1, 0), }; - for (Location location : AROUND_YOU) { - // The graphic is meant to play on a 3x3 radius around you, but not - // including the tile you are on. - player.getPacketDispatch().sendGlobalPositionGraphic(263, location); - } - - } - } diff --git a/Server/src/main/java/core/game/interaction/object/PriestInPerilOptionPlugin.java b/Server/src/main/java/core/game/interaction/object/PriestInPerilOptionPlugin.java index 47e17a152..ba1b8d79f 100644 --- a/Server/src/main/java/core/game/interaction/object/PriestInPerilOptionPlugin.java +++ b/Server/src/main/java/core/game/interaction/object/PriestInPerilOptionPlugin.java @@ -14,6 +14,7 @@ import core.game.node.object.Scenery; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.NPCs; /** * Represents the quest node plugin handler. @@ -200,7 +201,7 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } break; case "talk-through": - player.getDialogueInterpreter().open(1047, NPC.create(1047, player.getLocation())); + player.getDialogueInterpreter().open(NPCs.DREZEL_7690, NPC.create(NPCs.DREZEL_7690, player.getLocation())); break; } break; diff --git a/Server/src/main/java/core/game/node/entity/Entity.java b/Server/src/main/java/core/game/node/entity/Entity.java index 68f826c75..8d2046eba 100644 --- a/Server/src/main/java/core/game/node/entity/Entity.java +++ b/Server/src/main/java/core/game/node/entity/Entity.java @@ -638,7 +638,6 @@ public abstract class Entity extends Node { public Properties getProperties() { return properties; } - /** * Gets the updateMasks. * @return The updateMasks. diff --git a/Server/src/main/java/core/game/node/entity/combat/DeathTask.java b/Server/src/main/java/core/game/node/entity/combat/DeathTask.java index d2286f619..83922c552 100644 --- a/Server/src/main/java/core/game/node/entity/combat/DeathTask.java +++ b/Server/src/main/java/core/game/node/entity/combat/DeathTask.java @@ -55,8 +55,9 @@ public final class DeathTask extends NodeTask { } } } - e.getAnimator().forceAnimation(e.getProperties().getDeathAnimation()); e.graphics(Animator.RESET_G); + e.visualize(e.getProperties().getDeathAnimation(), e.getProperties().deathGfx); + e.getAnimator().forceAnimation(e.getProperties().getDeathAnimation()); e.commenceDeath(killer); e.getImpactHandler().setDisabledTicks(50); } diff --git a/Server/src/main/java/core/game/node/entity/impl/Properties.java b/Server/src/main/java/core/game/node/entity/impl/Properties.java index e2438f442..081ca3c4f 100644 --- a/Server/src/main/java/core/game/node/entity/impl/Properties.java +++ b/Server/src/main/java/core/game/node/entity/impl/Properties.java @@ -12,6 +12,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; import rs09.game.node.entity.combat.CombatPulse; import rs09.game.system.config.ItemConfigParser; import rs09.game.system.config.NPCConfigParser; @@ -92,6 +93,8 @@ public final class Properties { */ private Animation deathAnimation = new Animation(9055, Animator.Priority.HIGH); + public Graphics deathGfx = new Graphics(-1); + /** * The range animation. */ diff --git a/Server/src/main/java/core/game/node/entity/npc/NPC.java b/Server/src/main/java/core/game/node/entity/npc/NPC.java index b7cd9fc23..9486ce73f 100644 --- a/Server/src/main/java/core/game/node/entity/npc/NPC.java +++ b/Server/src/main/java/core/game/node/entity/npc/NPC.java @@ -25,6 +25,7 @@ import core.game.world.map.RegionManager; import core.game.world.map.build.DynamicRegion; import core.game.world.map.path.Pathfinder; import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.npc.NPCFaceEntity; import core.game.world.update.flag.npc.NPCFaceLocation; import core.game.world.update.flag.npc.NPCForceChat; @@ -305,6 +306,9 @@ public class NPC extends Entity { if (definition.getConfiguration("movement_radius") != null) { this.setWalkRadius(definition.getConfiguration("movement_radius")); } + if(definition.getConfiguration("death_gfx") != null) { + getProperties().deathGfx = new Graphics(definition.getConfiguration("death_gfx")); + } } /** diff --git a/Server/src/main/java/core/game/node/entity/player/Player.java b/Server/src/main/java/core/game/node/entity/player/Player.java index e119932bc..f0602227b 100644 --- a/Server/src/main/java/core/game/node/entity/player/Player.java +++ b/Server/src/main/java/core/game/node/entity/player/Player.java @@ -80,7 +80,6 @@ import rs09.game.ge.PlayerGrandExchange; import rs09.game.node.entity.combat.CombatSwingHandler; import rs09.game.node.entity.combat.equipment.EquipmentDegrader; import rs09.game.node.entity.skill.runecrafting.PouchManager; -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; import rs09.game.node.entity.state.newsys.State; import rs09.game.node.entity.state.newsys.StateRepository; import rs09.game.world.GameWorld; @@ -95,7 +94,7 @@ import java.util.*; import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_BASE; import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_DEATHS; -import static rs09.tools.stringtools.StringToolsKt.colorize; +import static rs09.tools.stringtools.GlobalsKt.colorize; /** * Represents a player entity. diff --git a/Server/src/main/java/core/game/node/entity/player/link/PacketDispatch.java b/Server/src/main/java/core/game/node/entity/player/link/PacketDispatch.java index 85db4e86f..3b9076515 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/PacketDispatch.java +++ b/Server/src/main/java/core/game/node/entity/player/link/PacketDispatch.java @@ -322,7 +322,7 @@ public final class PacketDispatch { * @param location the location. */ public void sendPositionedGraphic(int id, int height, int delay, Location location) { - PacketRepository.send(PositionedGraphic.class, new PositionedGraphicContext(player, new Graphics(id, height, delay), location)); + PacketRepository.send(PositionedGraphic.class, new PositionedGraphicContext(player, new Graphics(id, height, delay), location, 0, 0)); } /** @@ -342,8 +342,7 @@ public final class PacketDispatch { * @param location the location. */ public void sendPositionedGraphics(Graphics graphics, Location location) { - PacketRepository.send(PositionedGraphic.class, new PositionedGraphicContext(player, graphics, location)); - + PacketRepository.send(PositionedGraphic.class, new PositionedGraphicContext(player, graphics, location, 0, 0)); } /** diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeContainer.java b/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeContainer.java index 25a0d86cc..0a62d001b 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeContainer.java +++ b/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeContainer.java @@ -11,7 +11,7 @@ import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; import core.net.packet.out.ContainerPacket; -import static rs09.tools.stringtools.StringToolsKt.colorize; +import static rs09.tools.stringtools.GlobalsKt.colorize; /** * Represents the container during a trade session. diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/chapel/BoneOfferPlugin.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/chapel/BoneOfferPlugin.java index 8cccb5592..28139bea9 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/chapel/BoneOfferPlugin.java +++ b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/chapel/BoneOfferPlugin.java @@ -87,9 +87,12 @@ public class BoneOfferPlugin extends UseWithHandler { return; } final Location start = player.getLocation(); + + Location gfxLoc = player.getLocation().transform(player.getDirection(), 1); + ContentAPI.submitIndividualPulse(player, new Pulse(1) { int counter = 0; - + @Override public boolean pulse() { counter++; @@ -97,7 +100,7 @@ public class BoneOfferPlugin extends UseWithHandler { if (player.getInventory().remove(new Item(b.getItemId()))) { player.animate(ANIM); player.getAudioManager().send(new Audio(958)); - player.getPacketDispatch().sendPositionedGraphics(GFX, altar.getLocation()); + player.getPacketDispatch().sendPositionedGraphics(GFX, gfxLoc); player.sendMessage(getMessage(isLit(left), isLit(right))); player.getSkills().addExperience(Skills.PRAYER, b.getExperience() * getMod(altar, isLit(left), isLit(right))); } diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotion.java b/Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotion.java index b0ddfeae8..dc808863a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotion.java +++ b/Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotion.java @@ -1,6 +1,7 @@ package core.game.node.entity.skill.herblore; import core.game.node.item.Item; +import org.rs09.consts.Items; /** * Represents a finished potion. @@ -21,7 +22,7 @@ public enum FinishedPotion { SUPER_ANTIPOISON(UnfinishedPotion.IRIT, new Item(235), 48, 106.3, new Item(181)), FISHING_POTION(UnfinishedPotion.AVANTOE, new Item(231), 50, 112.5, new Item(151)), SUPER_ENERGY(UnfinishedPotion.AVANTOE, new Item(2970), 52, 117.5, new Item(3018)), - HUNTING_POTION(UnfinishedPotion.AVANTOE, new Item(10109), 53, 120, new Item(10000)), + HUNTING_POTION(UnfinishedPotion.AVANTOE, new Item(Items.KEBBIT_TEETH_DUST_10111), 53, 120, new Item(10000)), SUPER_STRENGTH(UnfinishedPotion.KWUARM, new Item(225), 55, 125, new Item(157)), WEAPON_POISON(UnfinishedPotion.KWUARM, new Item(241), 60, 137.5, new Item(187)), SUPER_RESTORE(UnfinishedPotion.SNAPDRAGON, new Item(223), 63, 142.5, new Item(3026)), diff --git a/Server/src/main/java/core/game/world/map/Location.java b/Server/src/main/java/core/game/world/map/Location.java index 8b7f8ddbc..17b22e7c0 100644 --- a/Server/src/main/java/core/game/world/map/Location.java +++ b/Server/src/main/java/core/game/world/map/Location.java @@ -6,6 +6,8 @@ import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; import core.tools.RandomFunction; +import java.util.ArrayList; + /** * Represents a location on the world map. * @author Emperor @@ -250,6 +252,24 @@ public final class Location extends Node { return Math.sqrt(xdiff * xdiff + ydiff * ydiff); } + /** + * Gets the 8 tiles surrounding this location as an ArrayList + */ + public ArrayList getSurroundingTiles() { + ArrayList locs = new ArrayList<>(); + + locs.add(transform(0,1,0)); //N + locs.add(transform(1,1,0)); //NE + locs.add(transform(1,0,0)); //E + locs.add(transform(1,-1,0)); //SE + locs.add(transform(0,-1,0)); //S + locs.add(transform(-1,-1,0));//SW + locs.add(transform(-1,0,0));//W + locs.add(transform(-1,1,0));//NW + + return locs; + } + /** * Gets the x position on the region chunk. * @return The x position on the region chunk. @@ -339,7 +359,7 @@ public final class Location extends Node { * @return The local y-coordinate. */ public int getSceneY(Location loc) { - return y - ((loc.getRegionY() - 6) * 8); + return y - ((loc.getRegionY() - 6) << 3); } /** diff --git a/Server/src/main/java/core/net/packet/context/PositionedGraphicContext.java b/Server/src/main/java/core/net/packet/context/PositionedGraphicContext.java index d26116617..738eeea0b 100644 --- a/Server/src/main/java/core/net/packet/context/PositionedGraphicContext.java +++ b/Server/src/main/java/core/net/packet/context/PositionedGraphicContext.java @@ -26,16 +26,23 @@ public final class PositionedGraphicContext implements Context { */ private final Location location; + public int sceneX, sceneY; + public int offsetX, offsetY; + /** * Constructs a new {@code PositionedGraphicContext} {@code Object}. * @param player The player. * @param graphic The graphic to display on the given location. * @param location The location to display the graphic on. */ - public PositionedGraphicContext(Player player, Graphics graphic, Location location) { + public PositionedGraphicContext(Player player, Graphics graphic, Location location, int offsetX, int offsetY) { this.player = player; this.graphic = graphic; this.location = location; + this.sceneX = location.getSceneX(player.getPlayerFlags().getLastSceneGraph()); + this.sceneY = location.getSceneY(player.getPlayerFlags().getLastSceneGraph()); + this.offsetX = offsetX; + this.offsetY = offsetY; } @Override diff --git a/Server/src/main/java/core/net/packet/in/InteractionPacket.java b/Server/src/main/java/core/net/packet/in/InteractionPacket.java index 9b93745f9..8973c4049 100644 --- a/Server/src/main/java/core/net/packet/in/InteractionPacket.java +++ b/Server/src/main/java/core/net/packet/in/InteractionPacket.java @@ -338,7 +338,7 @@ public final class InteractionPacket implements IncomingPacket { player.debug("Handled by quest interaction manager."); return; } - if(InteractionListeners.run(item.getId(), InteractionListener.Companion.getITEM(),option.getName(),player,item)){ + if(InteractionListeners.run(item.getId(), InteractionListener.Companion.getGROUNDITEM(), option.getName(),player,item)){ return; } item.getInteraction().handle(player, option); diff --git a/Server/src/main/java/core/net/packet/out/PositionedGraphic.java b/Server/src/main/java/core/net/packet/out/PositionedGraphic.java index 2652632eb..2b7289157 100644 --- a/Server/src/main/java/core/net/packet/out/PositionedGraphic.java +++ b/Server/src/main/java/core/net/packet/out/PositionedGraphic.java @@ -5,6 +5,7 @@ import core.game.world.update.flag.context.Graphics; import core.net.packet.IoBuffer; import core.net.packet.OutgoingPacket; import core.net.packet.context.PositionedGraphicContext; +import rs09.game.system.SystemLogger; /** * The positioned graphic outgoing packet. @@ -16,8 +17,16 @@ public final class PositionedGraphic implements OutgoingPacket 100) ContentAPI.poofClear(this) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FILLIMAN_TARLOCK_1050) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSDrezelDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSDrezelDialogue.kt new file mode 100644 index 000000000..26a0872a3 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSDrezelDialogue.kt @@ -0,0 +1,120 @@ +package rs09.game.content.quest.members.naturespirit + +import api.ContentAPI +import core.game.content.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.audio.Audio +import core.game.system.task.Pulse +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Items +import rs09.game.content.dialogue.DialogueFile +import rs09.tools.END_DIALOGUE +import rs09.tools.stringtools.colorize + +class NSDrezelDialogue : DialogueFile() { + var questStage = 0 + override fun handle(componentID: Int, buttonID: Int) { + questStage = player!!.questRepository.getStage("Nature Spirit") + + if(questStage <= 5){ + when(stage){ + 0 -> options("Sorry, not interested...", "Well, what is it, I may be able to help?").also { stage++ } + 1 -> when(buttonID){ + 1 -> playerl(FacialExpression.NEUTRAL, "Sorry, not interested.").also { stage = END_DIALOGUE } + 2 -> playerl(FacialExpression.FRIENDLY, "Well, what is it, I may be able to help?").also { stage++ } + } + + 2 -> npcl(FacialExpression.HALF_THINKING, "There's a man called Filliman who lives in Mort Myre, I wonder if you could look for him? The swamps of Mort Myre are dangerous though, they're infested with Ghasts!").also { stage++ } + 3 -> options("Who is this Filliman?", "Where's Mort Myre?", "What's a Ghast?", "Yes, I'll go and look for him.", "Sorry, I don't think I can help.").also { stage++ } + 4 -> when(buttonID){ + 1 -> npcl(FacialExpression.NEUTRAL, "Filliman Tarlock is his full name and he's a Druid. He lives in Mort Myre much like a hermit, but there's many a traveller who he's helped.").also { stage-- } + 2 -> npcl(FacialExpression.NEUTRAL, "Mort Myre is a decayed and dangerous swamp to the south. It was once a beautiful forest but has since become filled with vile emanations from within Morytania.").also { stage = 6 } + 3 -> npcl(FacialExpression.NEUTRAL, "A Ghast is a poor soul who died in Mort Myre. They're undead of a special class, they're untouchable as far as I'm aware!").also { stage = 5 } + 4 -> playerl(FacialExpression.FRIENDLY, "Yes, I'll go and look for him.").also { stage = 10 } + 5 -> playerl(FacialExpression.NEUTRAL, "Sorry, I don't think I can help.").also { stage = END_DIALOGUE } + } + 5 -> npcl(FacialExpression.NEUTRAL, "Filliman knew how to tackle them, but I've not heard from him in a long time. Ghasts, when they attack, will devour any food you have. If you have no food, they'll draw their nourishment from you!").also { stage = 3 } + 6 -> npcl(FacialExpression.NEUTRAL, " We put a fence around it to stop unwary travellers going in. Anyone who dies in the swamp is forever cursed to haunt it as a Ghast. Ghasts attack travellers, turning food to rotten filth.").also { stage = 3 } + + 10 -> npcl(FacialExpression.NEUTRAL, "That's great, but it is very dangerous. Are you sure you want to do this?").also { stage++ } + 11 -> options("Yes, I'm sure.", "Sorry, I don't think I can help.").also { stage++ } + 12 -> when(buttonID){ + 1 -> playerl(FacialExpression.FRIENDLY, "Yes, I'm sure.").also { stage = 20 } + 2 -> playerl(FacialExpression.NEUTRAL, "Sorry, I don't think I can help.").also { stage = END_DIALOGUE } + } + + 20 -> npcl(FacialExpression.NEUTRAL, "That's great! Many Thanks! Now then, please be aware of the Ghasts, you cannot attack them, only Filliman knew how to take them on.").also { stage++ } + 21 -> npcl(FacialExpression.NEUTRAL, "Just run from them if you can. If you start to get lost, try to make your way back to the temple.").also { stage++ } + 22 -> { + ContentAPI.sendDoubleItemDialogue(player!!, Items.MEAT_PIE_2327, Items.APPLE_PIE_2323, "The cleric hands you some food.") + if(questStage == 0){ + repeat(3) { ContentAPI.addItemOrDrop(player!!, Items.MEAT_PIE_2327, 1) } + repeat(3) { ContentAPI.addItemOrDrop(player!!, Items.APPLE_PIE_2323, 1) } + player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, 5) + } + stage++ + } + 23 -> npcl(FacialExpression.NEUTRAL, "Please take this food to Filliman, he'll probably appreciate a bit of cooked food. Now, he's never revealed where he lives in the swamps but I guess he'd be to the south, search for him won't you?").also { stage++ } + 24 -> playerl(FacialExpression.FRIENDLY, "I'll do my very best, don't worry, if he's in there and he's still alive I'll definitely find him.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest("Nature Spirit").start(player!!) } + } + } + + else if(questStage == 15) { + when(stage){ + 0 -> playerl(FacialExpression.HALF_GUILTY, "I've found Filliman and you should prepare for some sad news.").also { stage++ } + 1 -> npcl(FacialExpression.HALF_GUILTY, "You mean... he's dead?").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "Well, er sort of. I got to his camp and I encountered a spirit of some kind. I don't think it was a Ghast, it tried to communicate with me, but made no sense, it was all 'ooooh' this and 'oooh' that.").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "Hmmm, that's very interesting, I seem to remember Father Aereck in Lumbridge and his predecessor Father Urhney having a similar issue. Though this is probably not related to your problem.").also { stage++ } + 4 -> npcl(FacialExpression.NEUTRAL, " I will pray that it wasn't the spirit of my friend Filliman, but some lost soul who needs some help. Please do let me know how you get on with it.").also { stage = END_DIALOGUE } + } + } + + else if(questStage == 35){ + when(stage){ + 0 -> playerl(FacialExpression.FRIENDLY, "Hello again! I'm helping Filliman, he plans to become a nature spirit. I have a spell to cast but first I need to be blessed. Can you bless me?").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "But you haven't sneezed!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "You're so funny! But can you bless me?").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "Very well my friend, prepare yourself for the blessings of Saradomin. Here we go!").also { stage++ } + 4 -> { + end() + player!!.lock(); + ContentAPI.submitIndividualPulse(player!!, BlessingPulse(npc!!, player!!)) + } + } + } + + else if(questStage == 40){ + npcl(FacialExpression.NEUTRAL, "There you go my friend, you're now blessed. It's funny, now I look at you, there seems to be something of the faith about you. Anyway, good luck with your quest!").also { stage = END_DIALOGUE; player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, 45) } + } + + else { + when(stage){ + 0 -> npcl(FacialExpression.NEUTRAL, "Hello, friend, how goes your quest with Filliman?").also { stage++ } + 1 -> playerl(FacialExpression.NEUTRAL, "Still working at it.").also { stage++ } + 2 -> npcl(FacialExpression.NEUTRAL, "Well enough! Do let me know when something develops!").also { stage = END_DIALOGUE } + } + } + } + +} + +private class BlessingPulse(val drezel: NPC, val player: Player) : Pulse(){ + var ticks = 0 + + override fun pulse(): Boolean { + when(ticks){ + 0 -> ContentAPI.animate(drezel, 1162).also { ContentAPI.spawnProjectile(drezel, player, 268); ContentAPI.playAudio(player, Audio(2674)) } + 2 -> ContentAPI.visualize(player, Animation(645), Graphics(267, 100)) + 4 -> ContentAPI.unlock(player).also { player.questRepository.getQuest("Nature Spirit").setStage(player, 40); return true } + } + ticks++ + return false + } + + override fun stop() { + super.stop() + ContentAPI.openDialogue(player, NSDrezelDialogue(), drezel) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSListeners.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSListeners.kt new file mode 100644 index 000000000..70e619568 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSListeners.kt @@ -0,0 +1,234 @@ +package rs09.game.content.quest.members.naturespirit + +import api.Container +import api.ContentAPI +import api.DialUtils +import core.game.content.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.GroundItem +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.world.map.Location +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import rs09.game.content.dialogue.DialogueFile +import rs09.game.content.global.action.PickupHandler +import rs09.game.interaction.InteractionListener +import rs09.game.node.entity.npc.other.MortMyreGhastNPC +import rs09.game.system.SystemLogger +import rs09.game.system.config.ShopParser +import rs09.tools.END_DIALOGUE + +class NSListeners : InteractionListener() { + + val GROTTO_TREE = 3517 + val GROTTO_ENTRANCE = 3516 + val GROTTO_ALTAR = 3520 + val NATURE_ALTAR = 3521 + val JOURNAL = Items.JOURNAL_2967 + val NATURE_STONE = 3527 + val FAITH_STONE = 3528 + val FREELY_GIVEN_STONE = 3529 + val WASHING_BOWL = Items.WASHING_BOWL_2964 + val MIRROR = Items.MIRROR_2966 + val SPELLCARD = Items.DRUIDIC_SPELL_2968 + val USED_SPELLCARD = Items.A_USED_SPELL_2969 + val FUNGUS = Items.MORT_MYRE_FUNGUS_2970 + val MIRROR_TAKEN = "/save:ns:mirror_taken" + val GROTTO_SEARCHED = "/save:ns:grotto_searched" + val WISHING_WELL = 28715 + val DRUID_POUCH = Items.DRUID_POUCH_2958 + val DRUID_POUCH_EMPTY = Items.DRUID_POUCH_2957 + val stones = intArrayOf(NATURE_STONE, FAITH_STONE, FREELY_GIVEN_STONE) + val items = intArrayOf(USED_SPELLCARD, FUNGUS) + + override fun defineListeners() { + on(GROTTO_TREE, SCENERY, "look-at"){player, _ -> + ContentAPI.sendMessage(player, "It looks like a tree on a large rock with roots trailing down to the ground.") + return@on true + } + + on(GROTTO_TREE, SCENERY, "search"){player, _ -> + if(!ContentAPI.getAttribute(player, GROTTO_SEARCHED, false) || !(ContentAPI.inInventory(player, JOURNAL) || ContentAPI.inBank(player, JOURNAL))){ + ContentAPI.sendItemDialogue(player, JOURNAL, "You search the strange rock. You find a knot and inside of it you discover a small tome. The words on the front are a bit vague, but you can make out the words 'Tarlock' and 'journal'.") + ContentAPI.addItemOrDrop(player, JOURNAL, 1) + ContentAPI.setAttribute(player, GROTTO_SEARCHED, true) + return@on true + } + return@on false + } + + on(GROTTO_ENTRANCE, SCENERY, "enter"){player, node -> + val questStage = player.questRepository.getQuest("Nature Spirit").getStage(player) + if(questStage < 55) { + val npc = core.game.node.entity.npc.NPC.create(NPCs.FILLIMAN_TARLOCK_1050, Location.create(3440, 3336, 0)) + npc.init() + } else if(questStage < 60) { + player.teleport(Location.create(3442, 9734, 0)) + } else if (questStage >= 60){ + player.teleport(Location.create(3442, 9734, 1)) + } + return@on true + } + + on(GROTTO_ALTAR, SCENERY,"search"){player, node -> + val stage = player.questRepository.getStage("Nature Spirit") + if(stage == 55){ + ContentAPI.openDialogue(player, FillimanCompletionDialogue(), NPC(NPCs.FILLIMAN_TARLOCK_1050)) + return@on true + } + + return@on false + } + + on(NATURE_STONE, SCENERY, "search"){player, _ -> + ContentAPI.sendDialogue(player, "You search the stone and find that it has some sort of nature symbol scratched into it.") + return@on true + } + + on(FAITH_STONE, SCENERY, "search"){player, _ -> + ContentAPI.sendDialogue(player, "You search the stone and find that it has some sort of faith symbol scratched into it.") + return@on true + } + + on(FREELY_GIVEN_STONE, SCENERY, "search"){player, _ -> + ContentAPI.sendDialogue(player, "You search the stone and find it has some sort of spirit symbol scratched into it.") + return@on true + } + + on(WISHING_WELL, SCENERY, "make-wish"){player, node -> + if(player.questRepository.isComplete("Nature Spirit") && player.questRepository.isComplete("Wolf Whistle")) + ShopParser.openUid(player, 241) + else + ContentAPI.sendDialogue(player, "You can't do that yet.") + + return@on true + } + + on(JOURNAL, ITEM, "read"){player, _ -> + player.dialogueInterpreter.open(NSJournalDialogue()) + return@on true + } + + on(WASHING_BOWL, GROUNDITEM, "take"){player, node -> + SystemLogger.logInfo("Running listener") + GroundItemManager.create(Item(MIRROR), node.location, player) + PickupHandler.take(player, node as GroundItem) + return@on true + } + + on(MIRROR, GROUNDITEM, "take"){player, node -> + if(ContentAPI.getAttribute(player, MIRROR_TAKEN, false) && (ContentAPI.inInventory(player, MIRROR) || ContentAPI.inBank(player, MIRROR))){ + ContentAPI.sendDialogue(player, "I don't need another one of these.") + return@on true + } + ContentAPI.setAttribute(player, MIRROR_TAKEN, true) + PickupHandler.take(player, node as GroundItem) + return@on true + } + + on(SPELLCARD, ITEM, "cast"){player, node -> + if(NSUtils.castBloom(player)){ + ContentAPI.removeItem(player, node.asItem(), Container.INVENTORY) + ContentAPI.addItem(player, Items.A_USED_SPELL_2969) + } + return@on true + } + + on(intArrayOf(DRUID_POUCH, DRUID_POUCH_EMPTY), ITEM, "fill"){player, node -> + + if(player.questRepository.getStage("Nature Spirit") >= 75) { + if (ContentAPI.amountInInventory(player, Items.MORT_MYRE_FUNGUS_2970) >= 3) { + if (node.id != Items.DRUID_POUCH_2958) { + ContentAPI.removeItem(player, node, Container.INVENTORY) + } + ContentAPI.removeItem(player, Item(Items.MORT_MYRE_FUNGUS_2970, 3), Container.INVENTORY) + ContentAPI.addItem(player, Items.DRUID_POUCH_2958, 3) + } else { + ContentAPI.sendDialogue(player, "You need 3 fungus before you can do that.") + } + } else { + ContentAPI.sendDialogue(player, "I don't know how to use that yet.") + } + + return@on true + } + + onUseWith(SCENERY, Items.SILVER_SICKLE_2961, NATURE_ALTAR){player, used, with -> + ContentAPI.sendItemDialogue(player, Items.SILVER_SICKLEB_2963, "You dump the sickle into the waters.") + if(ContentAPI.removeItem(player, Items.SILVER_SICKLE_2961, Container.INVENTORY)){ + ContentAPI.addItem(player, Items.SILVER_SICKLEB_2963, 1) + } + return@onUseWith true + } + + onUseWith(NPC, DRUID_POUCH, NPCs.GHAST_1052){player, used, with -> + NSUtils.activatePouch(player, with as MortMyreGhastNPC) + } + + onUseWith(SCENERY, items, *stones) { player, used, with -> + when (used.id) { + USED_SPELLCARD -> { + if (with.id == FREELY_GIVEN_STONE) { + if(ContentAPI.removeItem(player, used, Container.INVENTORY)){ + ContentAPI.sendNPCDialogue(player, NPCs.FILLIMAN_TARLOCK_1050, "Aha, yes, that seems right well done!") + ContentAPI.sendMessage(player, "The stone seems to absorb the used spell scroll.") + NSUtils.flagCardPlaced(player) + } + } else ContentAPI.sendMessage(player, "You try to put the item on the stone, but it just moves off.") + } + + FUNGUS -> { + if (with.id == NATURE_STONE) { + if(ContentAPI.removeItem(player, used, Container.INVENTORY)){ + ContentAPI.sendNPCDialogue(player, NPCs.FILLIMAN_TARLOCK_1050, "Aha, yes, that seems right well done!") + ContentAPI.sendMessage(player, "The stone seems to absorb the used fungus.") + NSUtils.flagFungusPlaced(player) + } + } else ContentAPI.sendMessage(player, "You try to put the item on the stone, but it just moves off.") + } + } + return@onUseWith true + } + } +} + +class NSJournalDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> dialogue(*DialUtils.splitLines("Most of the writing is pretty uninteresting, but something inside refers to a nature spirit. The requirements for which are,")).also { stage++ } + 1 -> dialogue(*DialUtils.splitLines("'Something from nature', 'something with faith' and 'something of the spirit-to-become freely given'. It's all pretty vague.")).also { stage = END_DIALOGUE } + } + } +} + +class FillimanCompletionDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npcl(FacialExpression.NEUTRAL, "Well, hello there again. I was just enjoying the grotto. Many thanks for your help, I couldn't have become a Spirit of nature without you.").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "I must complete the transformation now. Just stand there and watch the show, apparently it's quite good!").also { stage++ } + 2 -> { + end() + ContentAPI.lock(player!!, 10) + ContentAPI.submitWorldPulse(CompleteSpellPulse(player!!)) + } + } + } +} + +class CompleteSpellPulse(val player: Player) : Pulse(2){ + var counter = 0 + val locations = arrayOf(Location.create(3444, 9740, 0), Location.create(3439, 9740, 0), Location.create(3439, 9737, 0), Location.create(3444, 9737, 0), Location.create(3444, 9735, 0), Location.create(3438, 9735, 0)) + val dest = Location.create(3441, 9738, 0) + override fun pulse(): Boolean { + when(counter++){ + 0 -> repeat(6) { ContentAPI.spawnProjectile(locations[it], dest, 268, 0, 1000, 0, 40, 20) } + 1 -> player.questRepository.getQuest("Nature Spirit").setStage(player, 60) + 2 -> player.teleport(player.location.transform(0,0,1)) + 3 -> ContentAPI.openDialogue(player, NPCs.NATURE_SPIRIT_1051, ContentAPI.findLocalNPC(player, NPCs.NATURE_SPIRIT_1051) as NPC).also { ContentAPI.unlock(player); return true } + } + return false + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSTarlockDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSTarlockDialogue.kt new file mode 100644 index 000000000..31f0b8260 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSTarlockDialogue.kt @@ -0,0 +1,241 @@ +package rs09.game.content.quest.members.naturespirit + +import api.Container +import api.ContentAPI +import api.DialUtils +import core.game.content.dialogue.DialoguePlugin +import core.game.content.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import rs09.tools.END_DIALOGUE + +@Initializable +class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { + var questStage = 0 + + override fun newInstance(player: Player?): DialoguePlugin { + return NSTarlockDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + val quest = player.questRepository.getQuest("Nature Spirit") + questStage = quest.getStage(player) + + if(questStage > 10 && !ContentAPI.inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ + npcl(FacialExpression.HALF_GUILTY, "OooOOOOOOoooOoOOoOOOo") + setQuest(15) + return false + } + + when(questStage){ + 10, 15 -> sendDialogue("A shifting apparition appears in front of you.").also { stage = 1000 } + 20 -> npcl(FacialExpression.HALF_GUILTY, "Oh, hello there, do you still think I'm dead? It's hard to see how I could be dead when I'm still in the world. I can see everything quite clearly. And nothing of what you say reflects the truth.") + 25 -> npcl(FacialExpression.HALF_GUILTY, "Oh, hello... Sorry, you've caught me at a bad time, it's just that I've had a sign you see and I need to find my journal.").also { stage = 7 } + 30 -> npcl(FacialExpression.HALF_GUILTY, "Thanks for the journal, I've been reading it. It looks like I came to a violent and bitter end but that's not really important. I just have to figure out what I am going to do now?").also { stage = 14 } + 35 -> npcl(FacialExpression.NEUTRAL, "Hello there, have you been blessed yet?").also { stage = 60 } + 45 -> { + if(ContentAPI.inInventory(player, Items.MORT_MYRE_FUNGUS_2970)){ + npcl(FacialExpression.NEUTRAL, "Did you manage to get something from nature?").also { stage = 80 } + } else { + playerl( + FacialExpression.NEUTRAL, + "Hello, I've been blessed but I don't know what to do now." + ).also { stage = 70 } + } + } + 50 -> npcl(FacialExpression.NEUTRAL, " Hello again! I don't suppose you've found out what the other components of the Nature spell are have you?").also { stage = 90 } + else -> npcl(FacialExpression.NEUTRAL, ".......").also { stage = END_DIALOGUE } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> if(ContentAPI.inInventory(player, Items.MIRROR_2966)){ + sendDialogue("You use the mirror on the spirit","of the dead Filliman Tarlock.").also { stage++ } + } else { + playerl(FacialExpression.NEUTRAL, "Yes, I do think you're dead and I'll prove it somehow.").also { stage = 1002 } + } + + 1 -> playerl(FacialExpression.NEUTRAL, "Here take a look at this, perhaps you can see that you're utterly transparent now!").also { stage++ } + 2 -> sendDialogue("The spirit of Filliman reaches forwards and takes the mirror.").also { stage++ } + 3 -> npcl(FacialExpression.HALF_GUILTY, "Well, that is the most peculiar thing I've ever experienced. Strange how well it reflects the stagnant swamp behind me, but there is nothing of my own visage apparent.").also { stage++ } + 4 -> playerl(FacialExpression.NEUTRAL, "That's because you're dead! Dead as a door nail... Deader in fact... You bear a remarkable resemblance to wormbait! Err... No offence...").also { stage++ } + 5 -> npcl(FacialExpression.HALF_GUILTY, "I think you might be right my friend, though I still feel very much alive. It is strange how I still come to be here and yet I've not turned into a Ghast.").also { stage++ } + 6 -> npcl(FacialExpression.HALF_GUILTY, " It must be a sign... Yes a sign... I must try to find out what it means. Now, where did I put my journal?").also { stage++ } + 7 -> if(!ContentAPI.inInventory(player, Items.JOURNAL_2967)){ + playerl(FacialExpression.NEUTRAL, "Where did you put it?").also { stage++; setQuest(25) } + } else sendDialogue("You give the journal to Filliman Tarlock").also { ContentAPI.removeItem(player, Items.JOURNAL_2967, Container.INVENTORY); stage = 10; setQuest(30) } + + //no journal + 8 -> npcl(FacialExpression.HALF_GUILTY, "Well, if I knew that, I wouldn't still be looking for it. However, I do remember something about a knot? Perhaps I was meant to tie a knot or something?").also { stage = END_DIALOGUE } + + //has journal + 10 -> playerl(FacialExpression.NEUTRAL, "Here, I found this, maybe you can use it?").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "My journal! That should help to collect my thoughts.").also { stage++ } + 12 -> sendDialogue("~ The spirit starts leafing through the journal. ~", "~ He seems quite distant as he regards the pages. ~", "~ After some time the druid faces you again. ~").also {stage++} + 13 -> npcl(FacialExpression.HALF_GUILTY, "It's all coming back to me now. It looks like I came to a violent and bitter end but that's not important now. I just have to figure out what I am going to do now?").also { stage++ } + 14 -> options("Being dead, what options do you think you have?", "So, what's your plan?", "Well, good luck with that.", "How can I help?", "Ok thanks.").also { stage++ } + 15 -> when(buttonId){ + 1 -> playerl(FacialExpression.NEUTRAL, "Being dead, what options do you think you have? I'm not trying to be rude or anything, but it's not like you have many options is it? I mean, it's either up or down for you isn't it?").also { stage = 20 } + 2 -> playerl(FacialExpression.NEUTRAL, "So, what's your plan?").also { stage = 30 } + 3 -> playerl(FacialExpression.NEUTRAL, "Well, good luck with that.").also { stage = 40 } + 4 -> playerl(FacialExpression.NEUTRAL, "How can I help?").also { stage = 50 } + 5 -> playerl(FacialExpression.NEUTRAL, "Ok thanks.").also { stage = END_DIALOGUE } + } + + //Being dead, what options + 20 -> npcl(FacialExpression.HALF_GUILTY, "Hmm, well you're a poetic one aren't you. Your material world logic stands you in good stead... If you're standing in the material world...").also { stage = 14 } + + //what's your plan? + 30 -> npcl(FacialExpression.HALF_GUILTY, "In my former incarnation I was Filliman Tarlock, a great druid of some power. I spent many years in this place, which was once a forest and I would wish to protect it as a nature spirit.").also { stage = 14 } + + //good luck with that + 40 -> npcl(FacialExpression.HALF_GUILTY, "Won't you help me to become a nature spirit? I could really use your help!").also { stage = 14 } + + //How can I help? + 50 -> npcl(FacialExpression.HALF_GUILTY, "Will you help me to become a nature spirit? The directions for becoming one are a bit vague, I need three things but I know how to get one of them. Perhaps you can help collect the rest?").also { stage++ } + 51 -> playerl(FacialExpression.NEUTRAL, "I might be interested, what's involved?").also { stage++ } + 52 -> npcl(FacialExpression.HALF_GUILTY, "Well, the book says, that I need, and I quote:- 'Something with faith', 'something from nature' and the 'spirit-to-become' freely given'. Hmm, I know how to get something from nature.").also { stage++ } + 53 -> playerl(FacialExpression.NEUTRAL, "Well, that does seem a bit vague.").also { stage++ } + 54 -> npcl(FacialExpression.HALF_GUILTY, "Hmm, it does and I could understand if you didn't want to help. However, if you could perhaps at least get the item from nature, that would be a start. Perhaps we can figure out the rest as we go along.").also { stage++ } + 55 -> sendDialogue(*DialUtils.splitLines("The druid produces a small sheet of papyrus with some writing on it.")).also { ContentAPI.addItemOrDrop(player, Items.DRUIDIC_SPELL_2968); setQuest(35); stage++ } + 56 -> npcl(FacialExpression.NEUTRAL, "This spell needs to be cast in the swamp after you have been blessed. I'm afraid you'll need to go to the temple to the North and ask a member of the clergy to bless you.").also { stage++ } + 57 -> playerl(FacialExpression.NEUTRAL, "Blessed, what does that do?").also { stage++ } + 58 -> npcl(FacialExpression.NEUTRAL, "It is required if you're to cast this druid spell. Once you've cast the spell, you should find something from nature. Bring it back to me and then we'll try to figure out the other things we need.").also { stage = END_DIALOGUE } + + //have you been blessed yet + 60 -> playerl(FacialExpression.NEUTRAL, "No, not yet.").also { stage++ } + 61 -> npcl(FacialExpression.NEUTRAL, "Well, hurry up!").also { stage++ } + 62 -> if(ContentAPI.inInventory(player, Items.DRUIDIC_SPELL_2968) || ContentAPI.inBank(player, Items.DRUIDIC_SPELL_2968)) end() + else playerl(FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } + 63 -> npcl(FacialExpression.NEUTRAL, "Sure, but please look after this one.").also { stage++ } + 64 -> sendDialogue("The spirit of Filliman Tarlock gives you another bloom spell.").also { ContentAPI.addItemOrDrop(player, Items.DRUIDIC_SPELL_2968); stage = END_DIALOGUE } + + //I've been blessed + 70 -> npcl(FacialExpression.NEUTRAL, "Well, you need to bring 'something from nature', 'something with faith' and 'something of the spirit-to- become freely given.'").also { stage++ } + 71 -> playerl(FacialExpression.NEUTRAL, "Yeah, but what does that mean?").also { stage++ } + 72 -> npcl(FacialExpression.NEUTRAL, "Hmm, it is a conundrum, however, if you use that spell I gave you, you should be able to get from nature. Once you have that, we may be puzzle the rest out.").also { stage++ } + 73 -> if(!ContentAPI.inInventory(player, Items.DRUIDIC_SPELL_2968) && !ContentAPI.inBank(player, Items.DRUIDIC_SPELL_2968)){ + playerl(FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } + } else end() + 74 -> npcl(FacialExpression.NEUTRAL, "Sure, but please look after this one.").also { stage++ } + 75 -> sendDialogue("The spirit of Filliman Tarlock gives you","another bloom spell.").also { ContentAPI.addItem(player, Items.DRUIDIC_SPELL_2968); stage = END_DIALOGUE } + + //has fungus + 80 -> sendDialogue("You show the fungus to Filliman.").also { stage++ } + 81 -> playerl(FacialExpression.NEUTRAL, "Yes, I have a fungus here that I picked.").also { stage++ } + 82 -> npcl(FacialExpression.NEUTRAL, "Wonderful, the mushroom represents 'something from nature'. Now we need to work out what the other components of the spell are!").also { stage = 90; setQuest(50) } + + //pre-spell options + 90 -> options("What are the things that are needed?", "What should I do when I have those things?", "I think I've solved the puzzle!", "Could I have another bloom scroll please?", "Ok, thanks.").also { stage++ } + 91 -> when(buttonId){ + 1 -> playerl(FacialExpression.NEUTRAL, "What are the things that are needed?").also { stage = 100 } + 2 -> playerl(FacialExpression.NEUTRAL, "What should I do when I have those things?").also { stage = 110 } + 3 -> playerl(FacialExpression.FRIENDLY, "I think I've solved the puzzle!").also { stage = 120 } + 4 -> playerl(FacialExpression.FRIENDLY, "Can I have another bloom scroll please?").also { stage = 130 } + 5 -> playerl(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } + } + + //What things are needed? + 100 -> npcl(FacialExpression.NEUTRAL, "The three things are: 'Something with faith', 'something from nature' and 'something of the spirit-to-become freely given'.").also { stage++ } + 101 -> playerl(FacialExpression.FRIENDLY, " Ok, and 'something from nature' is the mushroom from the bloom spell you gave me?").also { stage++ } + 102 -> npcl(FacialExpression.FRIENDLY, "Yes, that's correct, that seems right to me. The other things we need are 'something with faith' and 'something of the spirit-to-become freely given.").also { stage++ } + 103 -> playerl(FacialExpression.NEUTRAL, "Do you have any ideas what those things are?").also { stage++ } + 104 -> npcl(FacialExpression.HALF_GUILTY, "I'm sorry my friend, but I do not.").also { stage = 90 } + + //What should I do when I have them? + 110 -> npcl(FacialExpression.NEUTRAL, "It says,.. 'to arrange upon three rocks around the spirit-to-become...'. Then I must cast a spell. As you can see, I've already placed the rocks.").also { stage++ } + 111 -> playerl(FacialExpression.NEUTRAL, "Can we just place the components on any rock?").also { stage++ } + 112 -> npcl(FacialExpression.NEUTRAL, "Well, the only thing the journal says is that 'something with faith stands south of the spirit-to-become', but I'm so confused now I don't really know what that means.").also { stage = 90 } + + //I think I've solved the puzzle! + 120 -> npcl(FacialExpression.NEUTRAL, "Oh really.. Have you placed all the items on the stones? Ok, well, let's try!").also { stage++ } + 121 -> sendDialogue("~ The druid attempts to cast a spell. ~").also { stage++ } + 122 -> { + ContentAPI.animate(npc, 812) + if(NSUtils.hasPlacedCard(player) && NSUtils.hasPlacedFungus(player) && NSUtils.onStone(player)){ + end() + player.lock() + val locations = arrayOf(Location.create(3439, 3336, 0), Location.create(3441, 3336, 0), Location.create(3440, 3335, 0)) + repeat(3) {i -> ContentAPI.spawnProjectile(locations[i], Location.create(3440, 3336, 0), 268, 0, 35, 0, 100, 20) } + ContentAPI.submitIndividualPulse(player, object : Pulse(4){ + override fun pulse(): Boolean { + ContentAPI.sendNPCDialogue(player, npc.originalId, "Aha, everything seems to be in place! You can come through now into the grotto for the final section of my transformation.") + setQuest(55) + ContentAPI.unlock(player) + return true + } + + override fun stop() { + ContentAPI.visualize(npc, -1, Graphics(266, 80)) + super.stop() + } + }) + } else { + npcl(FacialExpression.NEUTRAL, "Hmm, something still doesn't seem right. I think we need something more before we can continue.") + } + + stage = END_DIALOGUE + } + + 130 -> if(ContentAPI.inInventory(player, Items.DRUIDIC_SPELL_2968) || ContentAPI.inBank(player, Items.DRUIDIC_SPELL_2968)){ + npcl(FacialExpression.NEUTRAL, "No, you've already got one!").also { stage = END_DIALOGUE } + } else { + npcl(FacialExpression.NEUTRAL, "Sure, but look after this one.") + ContentAPI.addItem(player, Items.DRUIDIC_SPELL_2968) + stage = END_DIALOGUE + } + + //Initial dialogue + 1000 -> playerl(FacialExpression.HALF_ASKING, "Hello?").also { stage++ } + 1001 -> if(ContentAPI.inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ + npcl(FacialExpression.EXTREMELY_SHOCKED, "Oh, I understand you! At last, someone who doesn't just mumble. I understand what you're saying!").also { stage++ } + } else npcl(FacialExpression.HALF_GUILTY, "OooOOoOOoOOOOo.") + + 1002 -> options("I'm wearing an amulet of ghost speak!","How long have you been a ghost?", "What's it like being a ghost?", "Ok, thanks.").also { stage++ } + 1003 -> when(buttonId){ + 1 -> playerl(FacialExpression.NEUTRAL, "I'm wearing an amulet of ghost speak!").also { stage = 1010; setQuest(20) } + 2 -> playerl(FacialExpression.NEUTRAL, "How long have you been a ghost?").also { stage = 1020; setQuest(20) } + 3 -> playerl(FacialExpression.NEUTRAL, "What's it like being a ghost?").also { stage = 1030; setQuest(20) } + 4 -> playerl(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } + } + + 1010 -> npcl(FacialExpression.HALF_GUILTY, "Why you poor fellow, have you passed away and you want to send a message back to a loved one?").also { stage++ } + 1011 -> playerl(FacialExpression.HALF_THINKING, "Err.. Not exactly...").also { stage++ } + 1012 -> npcl(FacialExpression.HALF_GUILTY, "You have come to haunt my dreams until I pass on your message to a dearly loved one. I understand. Pray, tell me who would you like me to pass a message on to?").also { stage++ } + 1013 -> playerl(FacialExpression.NEUTRAL, "Ermm, you don't understand... It's just that...").also { stage++ } + 1014 -> npcl(FacialExpression.HALF_GUILTY, "Yes!").also { stage++ } + 1015 -> playerl(FacialExpression.NEUTRAL, "Well please don't be upset or anything... But you're the ghost!").also { stage++ } + 1016 -> npcl(FacialExpression.HALF_GUILTY, "Don't be silly now! That in no way reflects the truth!").also { stage = 1002 } + + 1020 -> npcl(FacialExpression.HALF_GUILTY, "What?! Don't be preposterous! I'm not a ghost! How could you say something like that?").also { stage++ } + 1021 -> playerl(FacialExpression.NEUTRAL, "But it's true, you're a ghost... well, at least that is to say, you're sort of not alive anymore.").also { stage++ } + 1022 -> npcl(FacialExpression.HALF_GUILTY, "Don't be silly, I can see you. I can see that tree. If I were dead, I wouldn't be able to see anything. What you say just doesn't reflect the truth. You'll have to try harder to put one over on me!").also { stage = 1002 } + + 1030 -> npcl(FacialExpression.HALF_GUILTY, "Oh, it's quite.... Oh... Trying to catch me out were you! Anyone can clearly see that I am not a ghost!").also { stage++ } + 1031 -> playerl(FacialExpression.NEUTRAL, "But you are a ghost, look at yourself! I can see straight through you! You're as dead as this swamp! Err... No offence or anything...").also { stage++ } + 1032 -> npcl(FacialExpression.HALF_GUILTY, "No I won't take offence because I'm not dead and I'm afraid you'll have to come up with some pretty conclusive proof before I believe it. What a strange dream this is.").also { stage = 1002 } + + + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FILLIMAN_TARLOCK_1050) + } + + fun setQuest(stage: Int){ + player.questRepository.getQuest("Nature Spirit").setStage(player, stage) + } + +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSUtils.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSUtils.kt new file mode 100644 index 000000000..0f07c86e2 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSUtils.kt @@ -0,0 +1,125 @@ +package rs09.game.content.quest.members.naturespirit + +import api.Container +import api.ContentAPI +import core.game.node.Node +import core.game.node.`object`.Scenery +import core.game.node.`object`.SceneryBuilder +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.map.RegionManager.forId +import core.game.world.map.RegionManager.lock +import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import org.rs09.consts.Items +import rs09.game.node.entity.npc.other.MortMyreGhastNPC + +object NSUtils { + fun flagFungusPlaced(player: Player) { + ContentAPI.setAttribute(player, "/save:ns:fungus_placed", true) + } + + fun flagCardPlaced(player: Player){ + ContentAPI.setAttribute(player, "/save:ns:card_placed", true) + } + + fun hasPlacedFungus(player: Player): Boolean { + return ContentAPI.getAttribute(player, "ns:fungus_placed", false) + } + + fun hasPlacedCard(player: Player): Boolean { + return ContentAPI.getAttribute(player, "ns:card_placed", false) + } + + fun onStone(player: Player): Boolean { + return player.location.equals(3440, 3335, 0) + } + + fun getGhastKC(player: Player): Int { + return ContentAPI.getAttribute(player,"ns:ghasts_killed", 0) as Int + } + + fun incrementGhastKC(player: Player){ + ContentAPI.setAttribute(player, "/save:ns:ghasts_killed", getGhastKC(player) + 1) + val msg = when(getGhastKC(player)) { + 1 -> "That's one down, two more to go." + 2 -> "Two down, only one more to go." + 3 -> "That's it! I've killed all 3 Ghasts!" + else -> "" + } + + if(!msg.isEmpty()){ + ContentAPI.sendMessage(player, msg) + } + } + + fun activatePouch(player: Player, attacker: MortMyreGhastNPC): Boolean { + var shouldAddEmptyPouch = false + val pouchAmt = ContentAPI.amountInInventory(player, Items.DRUID_POUCH_2958) + if(pouchAmt == 1) shouldAddEmptyPouch = true + if(pouchAmt > 0 && ContentAPI.removeItem(player, Items.DRUID_POUCH_2958, Container.INVENTORY)){ + if(shouldAddEmptyPouch){ + ContentAPI.addItem(player, Items.DRUID_POUCH_2957) + } + ContentAPI.spawnProjectile(player, attacker, 268) + ContentAPI.submitWorldPulse(object : Pulse(){ + var ticks = 0 + override fun pulse(): Boolean { + when(ticks++){ + 2 -> ContentAPI.visualize(attacker, -1, Graphics(269, 125)) + 3 -> attacker.transform(attacker.id + 1).also { attacker.attack(player); attacker.setAttribute("woke", ContentAPI.getWorldTicks()); return true } + } + return false + } + }) + return true + } + return false + } + + fun cleanupAttributes(player: Player){ + player.removeAttribute("ns:fungus_placed") + player.removeAttribute("ns:card_placed") + } + + @JvmStatic + fun castBloom(player: Player): Boolean{ + var success = false + val region = forId(player.location.regionId) + if (player.skills.prayerPoints < 1) { + player.packetDispatch.sendMessage("You don't have enough prayer points to do this.") + return false + } + handleVisuals(player) + val locs = player.location.surroundingTiles + for (o in locs) { + val obj = RegionManager.getObject(o) + if (obj != null) { + if (obj.name.equals("Rotting log", ignoreCase = true) && player.skills.prayerPoints >= 1) { + if (player.location.withinDistance(obj.location, 2)) { + SceneryBuilder.replace(obj, obj.transform(3509)) + success = true + } + } + } + } + return success + } + + /** + * Handles the draining of prayer points and physical graphics and + * animation. + */ + private fun handleVisuals(player: Player) { + player.skills.decrementPrayerPoints(RandomFunction.random(1, 3).toDouble()) + val AROUND_YOU = player.location.surroundingTiles + for (location in AROUND_YOU) { + // The graphic is meant to play on a 3x3 radius around you, but not + // including the tile you are on. + player.packetDispatch.sendGlobalPositionGraphic(263, location) + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritDialogue.kt new file mode 100644 index 000000000..052b699d5 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritDialogue.kt @@ -0,0 +1,164 @@ +package rs09.game.content.quest.members.naturespirit + +import api.Container +import api.ContentAPI +import core.game.content.dialogue.DialoguePlugin +import core.game.content.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import rs09.game.content.dialogue.DialogueFile +import rs09.tools.END_DIALOGUE + +@Initializable +class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ + + val questStage = player?.questRepository?.getStage("Nature Spirit") ?: 0 + override fun newInstance(player: Player?): DialoguePlugin { + return NatureSpiritDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + when(questStage){ + 60 -> npcl(FacialExpression.NEUTRAL, "Hmm, good, the transformation is complete. Now, my friend, in return for your assistance, I will help you to kill the Ghasts. First bring to me a silver sickle so that I can bless it for you.").also { return true } + 65 -> npcl(FacialExpression.NEUTRAL, "Have you brought me a silver sickle?").also { stage = 100; return true } + 70 -> npcl(FacialExpression.NEUTRAL, "Now you can go forth and make the swamp bloom. Collect nature's bounty to fill a druids pouch. So armed will the Ghasts be bound to you until, you flee or they are defeated.").also { stage = 200 } + 75 -> npcl(FacialExpression.NEUTRAL, "Hello again, my friend. Have you defeated three ghasts as I asked you?").also { stage = 300 } + else -> npcl(FacialExpression.FRIENDLY, "Welcome to my grotto, friend. Enjoy your visit.").also { stage = END_DIALOGUE } + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + when(stage){ + 0 -> playerl(FacialExpression.NEUTRAL,"A silver sickle? What's that?").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "The sickle is the symbol and weapon of the Druid, you need to construct one of silver so that I can bless it, with its powers you will be able to defeat the Ghasts of Mort Myre.").also { stage++; setQuest(65) } + 2 -> options("Where would I get a silver sickle?", "What will you do to the silver sickle?", "How can a blessed sickle help me to defeat the Ghasts?", "Ok, thanks.").also { stage++ } + 3 -> when(buttonID){ + 1 -> playerl(FacialExpression.NEUTRAL, "Where would I get a silver sickle?").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "What will you do to the silver sickle?").also { stage = 20 } + 3 -> playerl(FacialExpression.NEUTRAL, "How can a blessed sickle help me to defeat the Ghasts?").also { stage = 30 } + 4 -> playerl(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } + } + + //where sickle + 10 -> npcl(FacialExpression.NEUTRAL, "You could make one yourself if you're artisan enough. I've heard of a distant sandy place where you can buy the mould that you require, it's similar in many respects to the creating of a holy symbol.").also { stage = 2 } + + //What you gonna do to my sickle bro + 20 -> npcl(FacialExpression.NEUTRAL, "Why, I will give it my blessings so that the very swamp in which you stand will blossom and bloom!").also { stage = 2 } //pompous git + + //bruh how does a silver sickle help me tho + 30 -> npcl(FacialExpression.NEUTRAL, "My blessings will entice nature to bloom in Mort Myre! And then with nature's harvest you can fill a druids pouch and release the Ghasts from their torment.").also { stage = 2 } //this dude kinda weird + + //have you brought me a sickle yet bro + 100 -> if(ContentAPI.inInventory(player, Items.SILVER_SICKLE_2961)){ + playerl(FacialExpression.FRIENDLY, "Yes, here it is. What are you going to do with it?").also { stage = 110 } + } else { + playerl(FacialExpression.NEUTRAL, "No sorry, not yet!").also { stage++ } + } + 101 -> npcl(FacialExpression.NEUTRAL, "Well, come to me when you have it.").also { stage = 2 } + + /** + * This dialogue drags on so much man this quest has been like 95% dialogue. + * Nature Spirit dude also talks like an uppity self-righteous deity looking dude + */ + + //yeah bro I got it + 110 -> npcl(FacialExpression.NEUTRAL, "My friend, I will bless it for you and you will then be able to accomplish great things. Now then, I must cast the enchantment. You can bless a new sickle by dipping it in the holy water of the grotto.").also { stage++ } + 111 -> sendDialogue("- The Nature Spirit casts a spell on the player. -").also { stage++ } + + /** + * Here we go uoooh + */ + 112 -> end().also { ContentAPI.lock(player, 10); ContentAPI.submitWorldPulse(SickleBlessPulse(player, npc)) } + + //go kill some ghasts bro + 200 -> npcl(FacialExpression.NEUTRAL, "Go forth into Mort Myre and slay three Ghasts. You'll be releasing their souls from Mort Myre.").also { stage++ } + 201 -> ContentAPI.sendItemDialogue(player, Items.DRUID_POUCH_2957, "The nature spirit gives you an empty pouch.").also { stage++; setQuest(75) } + 202 -> npcl(FacialExpression.NEUTRAL, "You'll need this in order to collect together nature's bounty. It will bind the Ghast to you until you flee or it is defeated.").also { stage = END_DIALOGUE } + + //Have you killed the ghasts yet bro + 300 -> if(NSUtils.getGhastKC(player) >= 3){ + playerl(FacialExpression.NEUTRAL, "Yes, I've killed all three and their spirits have been released!").also { stage = 350 } + } else { + playerl(FacialExpression.NEUTRAL, "Not yet.").also { stage++ } + } + + //nah bro + 301 -> npcl(FacialExpression.NEUTRAL, "Well, when you do, please come to me and I'll reward you!").also { stage++ } + 302 -> options("How do I get to attack the Ghasts?", "What's this pouch for?", "What can I do with this sickle?", "I've lost my sickle.", "Ok, thanks.").also { stage++ } + 303 -> when(buttonID){ + 1 -> playerl(FacialExpression.NEUTRAL, "How do I get to attack the Ghasts?").also { stage = 310 } + 2 -> playerl(FacialExpression.NEUTRAL, "What's this pouch for?").also { stage = 320 } + 3 -> playerl(FacialExpression.NEUTRAL, "What can I do with this sickle?").also { stage = 330 } + 4 -> playerl(FacialExpression.NEUTRAL, "I've lost my sickle.").also { stage = 340 } + 5 -> playerl(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } + } + + //How do I attack duh ghosty bois + 310 -> npcl(FacialExpression.NEUTRAL, "Go forth and with the sickle make the swamp bloom. Collect natures bounty to fill a druids pouch. So armed will the Ghasts be bound to you until, you flee or they are defeated.").also { stage = 302 } + + //What's dis funny pouch for? + 320 -> npcl(FacialExpression.NEUTRAL, "It is for collecting natures bounty, once it contains the blossomed items of the swamp, it will make the Ghasts appear and you can then attack them.").also { stage = 302 } + + //What can I do wif da sickle m8 + 330 -> npcl(FacialExpression.NEUTRAL, "You may use it wisely within the area of Mort Myre to affect natures balance and bring forth a bounty of natures harvest. Once collected into the druid pouch will the Ghast be apparent.").also { stage = 302 } + + //oi I lost it bruv + 340 -> npcl(FacialExpression.NEUTRAL, "If you should lose the blessed sickle, simply bring another to my altar of nature and refresh it in the grotto waters.").also { stage = 302 } + + //killed all dem buggers bruv + 350 -> npcl(FacialExpression.NEUTRAL, "Many thanks my friend, you have completed your quest!").also { stage++ } + 351 -> end().also { player.questRepository.getQuest("Nature Spirit").finish(player) } + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.NATURE_SPIRIT_1051) + } + + /** + * Needs to spawn 4ish of those green projectiles and the player needs to lift up the sickle + * then needs to open the dialogue back up with the quest stage at 70 + */ + internal class SickleBlessPulse(val player: Player, val spirit: NPC) : Pulse() { + var ticks = 0 + val locs: MutableList = player.location.surroundingTiles + + override fun pulse(): Boolean { + when(ticks++){ + 0 -> ContentAPI.animate(spirit, 812) + 1 -> repeat(4) { + val loc = locs.random() + locs.remove(loc) + + ContentAPI.spawnProjectile(loc, player.location, 268, 0, 400, 0, 125, 180) + ContentAPI.animate(player, 9021) + } + 4 -> { + if(ContentAPI.removeItem(player, Items.SILVER_SICKLE_2961, Container.INVENTORY)){ + ContentAPI.addItem(player, Items.SILVER_SICKLEB_2963) + ContentAPI.unlock(player) + player.questRepository.getQuest("Nature Spirit").setStage(player, 70) + ContentAPI.openDialogue(player, NPCs.NATURE_SPIRIT_1051, ContentAPI.findLocalNPC(player, NPCs.NATURE_SPIRIT_1051) as NPC) + ContentAPI.sendMessage(player, "Your sickle has been blessed! You can bless a new sickle by dipping it into the grotto waters.") + } + } + 6 -> return true + } + return false + } + } + + fun setQuest(stage: Int){ + player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, stage) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritQuest.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritQuest.kt new file mode 100644 index 000000000..05ac35cbc --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritQuest.kt @@ -0,0 +1,122 @@ +package rs09.game.content.quest.members.naturespirit + +import api.ContentAPI +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +@Initializable +class NatureSpiritQuest : Quest("Nature Spirit", 95, 94, 2, 307, 0, 1, 110 ) { + override fun newInstance(`object`: Any?): Quest { + return this + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + player ?: return + var line = 11 + if(stage == 0){ + line(player, "I can start this quest by speaking to !!Drezel?? in the !!temple of Saradomin??.", line++) + } else { + if(stage >= 10){ + line(player, "After talking to Drezel in the temple of Saradomin I've",line++, true) + line(player,"agreed to look for a Druid called Filliman Tarlock.", line++, true) + } + + if(stage == 10){ + line(player, "I need to look for !!Filliman Tarlock?? in the !!Swamps?? of Mort",line++) + line(player, "Myre. I should be wary of !!Ghasts??.", line++) + } + + if(stage == 15){ + line(player, "I located a !!spirit?? in the swamp. I believe he's", line++, false) + line(player, "!!Filliman Tarlock?? but I can't understand him.",line++, false) + } + + if(stage == 20){ + line(player, "I located !!Filliman Tarlock?? in the swamp. I believe he's",line++) + line(player, "dead but he doesn't believe me. I need to convince him.", line++) + } + + if(stage >= 25){ + line(player, "I located Filliman Tarlock in the swamp and managed to",line++,true) + line(player, "convince him that he is in fact a ghost. ", line++, true) + } + + if(stage == 25){ + line(player, "Filliman needs his !!journal?? to figure out what to do",line++) + line(player, "next. He mentioned something about a !!knot??.", line++) + } + + if(stage >= 30){ + line(player, "I recovered Filliman's journal for him.", line++, true) + } + + if(stage == 30) { + line(player, "I should speak to !!Filliman Tarlock?? to see what I can",line++) + line(player, "do to help.", line++) + } + + if(stage >= 40){ + line(player, "I've gone and gotten blessed by Drezel.", line++, true) + } + + if(stage >= 35) { + line(player, "I've agreed to help Filliman become a Nature Spirit.",line++, true) + } + + if(stage == 35){ + line(player, "The first thing Filliman needs me to do is go and get",line++) + line(player, "blessed by !!Drezel?? in the temple of Saradomin.",line++) + } + + if (stage == 40){ + line(player, "I should return to !!Filliman?? to see what I need to do.", line++, false) + } + + if(stage in 45 until 55){ + line(player, "In order to help Filliman I need to find 3 things:", line++, false) + line(player, "Something of !!Faith??.",line++, false) + line(player, "Something of !!Nature??.", line++, stage >= 50) + line(player, "Something of the !!spirit-to-be freely given??.", line++, false) + } + + if(stage == 50){ + line(player, "I know for a fact the fungus is !!something of Nature??.", line++, false) + } + + if(stage >= 55){ + line(player, "I've helped Filliman complete the spell.", line++, true) + } + + if(stage == 55){ + line(player, "Filliman has asked me to meet him back inside the !!grotto??.", line++, false) + } + + if(stage == 75){ + line(player, "I need to go and kill !!3 Ghasts?? for Filliman.", line++, false) + } + + if(stage >= 100){ + line(player,"!!QUEST COMPLETE!??",line++) + } + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.SILVER_SICKLEB_2963,230,277,5) + drawReward(player, "2 Quest Points", ln++) + drawReward(player, "3,000 Crafting XP",ln++) + drawReward(player, "2,000 Hitpoints XP", ln++) + drawReward(player, "2,000 Defence XP", ln++) + ContentAPI.rewardXP(player, Skills.CRAFTING, 3000.0) + ContentAPI.rewardXP(player, Skills.HITPOINTS, 2000.0) + ContentAPI.rewardXP(player, Skills.DEFENCE, 2000.0) + NSUtils.cleanupAttributes(player) + } +} diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt index 7526cc706..6f93c9334 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt @@ -9,6 +9,7 @@ abstract class InteractionListener : Listener{ val ITEM = 0 val SCENERY = 1 val NPC = 2 + val GROUNDITEM = 3 } fun on(id: Int, type: Int, vararg option: String,handler: (player: Player, node: Node) -> Boolean){ InteractionListeners.add(id,type,option,handler) diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt index 415b2b56c..65fdcac52 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt @@ -170,6 +170,7 @@ object InteractionListeners { @JvmStatic fun run(id: Int, type: Int, option: String, player: Player, node: Node): Boolean{ val flag = when(type){ + 3 -> DestinationFlag.ITEM 2 -> DestinationFlag.ENTITY 1 -> DestinationFlag.OBJECT else -> DestinationFlag.OBJECT diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/MorytaniaListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/MorytaniaListeners.kt index 8d301b34e..3547a1ed3 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/region/MorytaniaListeners.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/region/MorytaniaListeners.kt @@ -15,7 +15,7 @@ import rs09.game.interaction.InteractionListener class MorytaniaListeners : InteractionListener() { val GROTTO_ENTRANCE = 3516 - val GROTTO_EXIT = 3526 + val GROTTO_EXIT = intArrayOf(3525, 3526) val GROTTO_BRIDGE = 3522 val outside = Location.create(3439, 3337, 0) val inside = Location.create(3442, 9734, 1) @@ -23,10 +23,10 @@ class MorytaniaListeners : InteractionListener() { private val JUMP_ANIM = Animation(1603) override fun defineListeners() { - on(GROTTO_ENTRANCE,SCENERY,"enter"){ player, node -> +/* on(GROTTO_ENTRANCE,SCENERY,"enter"){ player, node -> player.teleport(inside) return@on true - } + }*/ on(GROTTO_EXIT,SCENERY,"exit"){ player, node -> player.teleport(outside) diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/MortMyreGhastNPC.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/other/MortMyreGhastNPC.kt new file mode 100644 index 000000000..5aa192d43 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/npc/other/MortMyreGhastNPC.kt @@ -0,0 +1,104 @@ +package rs09.game.node.entity.npc.other + +import api.Container +import api.ContentAPI +import core.game.content.consumable.Consumables +import core.game.content.consumable.Food +import core.game.interaction.MovementPulse +import core.game.node.entity.Entity +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import core.tools.RandomFunction +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import rs09.game.content.quest.members.naturespirit.NSUtils + +@Initializable +class MortMyreGhastNPC : AbstractNPC { + //Constructor spaghetti because Arios I guess + constructor() : super(NPCs.GHAST_1052, null, true) {} + private constructor(id: Int, location: Location) : super(id, location) {} + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + isAggressive = id != ids[0] + return MortMyreGhastNPC(id, location) + } + + override fun handleTickActions() { + super.handleTickActions() + if(id == ids[0] && RandomFunction.roll(35)){ + val players = RegionManager.getLocalPlayers(this, 5).filter { !it.inCombat() } + if(players.isNotEmpty()){ + val player = players.random() + ContentAPI.submitIndividualPulse(this, object : MovementPulse(this, player){ + override fun pulse(): Boolean { + animate(Animation(1093)) + attemptLifeSiphon(player) + return true + } + }) + } + } else { + val ticksTransformed = ContentAPI.getWorldTicks() - ContentAPI.getAttribute(this, "woke", 0) + if(!inCombat() && ticksTransformed > 10){ + reTransform() + } + } + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GHAST_1052, NPCs.GHAST_1053) + } + + fun attemptLifeSiphon(player: Player){ + var hasFood = false + + if(NSUtils.activatePouch(player, this)) return + + GlobalScope.launch { + for(i in player.inventory.toArray()){ + if(i == null) continue + val consumable = Consumables.getConsumableById(i.id) + if(consumable != null && consumable is Food) { + hasFood = true + ContentAPI.removeItem(player, i, Container.INVENTORY) + ContentAPI.addItem(player, Items.ROTTEN_FOOD_2959) + ContentAPI.sendMessage(player, "You feel something attacking your backpack, and smell a terrible stench.") + break + } + } + + if(!hasFood && RandomFunction.roll(3)) { + ContentAPI.sendMessage(player, "An attacking Ghast just misses you.") + } else if(!hasFood){ + ContentAPI.impact(player, RandomFunction.random(3,6), ImpactHandler.HitsplatType.NORMAL) + ContentAPI.sendMessage(player, "A supernatural force draws energy from you.") + } + } + } + + override fun commenceDeath(killer: Entity?) { + super.commenceDeath(killer) + } + + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + if(id == ids[1]) { + reTransform() + if(killer is Player){ + NSUtils.incrementGhastKC(killer) + ContentAPI.rewardXP(killer, Skills.PRAYER, 30.0) + removeAttribute("woke") + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/NPCConfigParser.kt b/Server/src/main/kotlin/rs09/game/system/config/NPCConfigParser.kt index 88e5ece5c..7200d3c7d 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/NPCConfigParser.kt +++ b/Server/src/main/kotlin/rs09/game/system/config/NPCConfigParser.kt @@ -245,6 +245,7 @@ NPCConfigParser { "prj_height", "end_height", "spell_id", + "death_gfx", "magic_level" -> configs.put(it.key.toString(), if (it.value.toString().isEmpty()) Unit else it.value.toString().toInt()) //doubles diff --git a/Server/src/main/kotlin/rs09/tools/stringtools/StringTools.kt b/Server/src/main/kotlin/rs09/tools/Globals.kt similarity index 100% rename from Server/src/main/kotlin/rs09/tools/stringtools/StringTools.kt rename to Server/src/main/kotlin/rs09/tools/Globals.kt From 8d16b3d4e42c43a88968aaf44d9cfc4d3047b680 Mon Sep 17 00:00:00 2001 From: ceikry Date: Mon, 16 Aug 2021 06:59:44 -0500 Subject: [PATCH 12/12] Add check for NS completion for silver sickle --- Server/data/eco/gedb.xml | 4 ++-- .../core/game/interaction/item/SilverSicklePlugin.java | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Server/data/eco/gedb.xml b/Server/data/eco/gedb.xml index 306861c2c..4f89c9973 100644 --- a/Server/data/eco/gedb.xml +++ b/Server/data/eco/gedb.xml @@ -2656,8 +2656,8 @@ 379 268 - 0 - 0 + 173 + 57782 0 diff --git a/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java b/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java index 3c0713edd..26629dd58 100644 --- a/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java +++ b/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java @@ -1,5 +1,6 @@ package core.game.interaction.item; +import api.ContentAPI; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -33,8 +34,12 @@ public final class SilverSicklePlugin extends OptionHandler { switch (option) { case "operate": case "cast bloom": - player.getPacketDispatch().sendAnimation(9021); - NSUtils.castBloom(player); + if(player.getQuestRepository().getQuest("Nature Spirit").getStage(player) >= 75) { + player.getPacketDispatch().sendAnimation(9021); + NSUtils.castBloom(player); + } else { + ContentAPI.sendDialogue(player, "You must complete Nature Spirit to use this."); + } return true; } return false;