Merge branch 2009scape:master into master

This commit is contained in:
Tooze 2026-01-27 14:21:18 +00:00
commit 17a31969ac
283 changed files with 10362 additions and 9282 deletions

0
.gitlab/.gitkeep Normal file
View file

View file

View file

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

View file

@ -8021,16 +8021,9 @@
}
],
"charm": [],
"ids": "128,1479,1875,4343",
"ids": "128,1479,1875",
"description": "",
"main": [
{
"minAmount": "1",
"weight": "25.0",
"id": "7862",
"maxAmount": "1"
}
]
"main": []
},
{
"default": [
@ -36458,12 +36451,6 @@
"weight": "100.0",
"id": "532",
"maxAmount": "1"
},
{
"minAmount": "3",
"weight": "100.0",
"id": "526",
"maxAmount": "3"
}
],
"charm": [],
@ -58779,5 +58766,25 @@
"ids": "8127",
"description": "Dark energy core",
"main": []
},
{
"default": [
{
"minAmount": "1",
"weight": "1.0",
"id": "6287",
"maxAmount": "1"
},
{
"minAmount": "1",
"weight": "1.0",
"id": "526",
"maxAmount": "1"
}
],
"charm": [],
"ids": "4343",
"description": "",
"main": []
}
]

View file

@ -207,6 +207,10 @@
"item_id": "590",
"loc_data": "{1,2368,3135,0,140}-{1,2431,3072,0,140}-{1,3112,3369,2,140}-{1,3209,3734,0,100}-"
},
{
"item_id": "600",
"loc_data": "{1,2438,3187,0,200}-"
},
{
"item_id": "677",
"loc_data": "{1,3369,3378,0,150}-"
@ -233,7 +237,7 @@
},
{
"item_id": "946",
"loc_data": "{1,2903,3148,0,80}-{1,3205,3212,0,80}-{1,3224,3202,0,80}-{1,3218,3416,1,90}-{1,2820,3450,0,90}-{1,2700,3407,0,100}-{1,3106,3956,0,60}-{1,2566,9526,0,30}-{1,3215,9625,0,80}-{1,3218,9887,0,33}-"
"loc_data": "{1,2903,3148,0,80}-{1,3205,3212,0,80}-{1,3224,3202,0,80}-{1,3218,3416,1,90}-{1,2820,3450,0,90}-{1,2700,3407,0,100}-{1,3106,3956,0,60}-{1,2566,9526,0,30}-{1,3215,9625,0,80}-{1,2653,9767,0,30}-{1,3218,9887,0,33}-"
},
{
"item_id": "952",
@ -417,11 +421,11 @@
},
{
"item_id": "1812",
"loc_data": "{1,2747,3579,0,100}"
"loc_data": "{1,2747,3579,0,100}-"
},
{
"item_id": "1813",
"loc_data": "{1,2746,3578,0,100}"
"loc_data": "{1,2746,3578,0,100}-"
},
{
"item_id": "1856",
@ -671,6 +675,10 @@
"item_id": "11065",
"loc_data": "{1,2928,3289,0,90}-"
},
{
"item_id": "11656",
"loc_data": "{1,2438,3185,0,200}-"
},
{
"item_id": "12494",
"loc_data": "{1,2762,2973,0,60}-"

View file

@ -5575,7 +5575,7 @@
"id": "601"
},
{
"destroy_message": "You'll have to get another down in the Dungeon",
"destroy_message": "You'll have to get another down in the dungeon.",
"examine": "An unusual clay mould in the shape of a disc.",
"durability": null,
"name": "Lens mould",
@ -39175,7 +39175,7 @@
"id": "4237"
},
{
"examine": "It's a bowl of (milky) nettle tea.",
"examine": "It's a bowl of nettle tea.",
"durability": null,
"name": "Nettle tea",
"weight": "0.9",
@ -39183,7 +39183,7 @@
"id": "4239"
},
{
"examine": "It's a bowl of (milky) nettle tea.",
"examine": "It's a bowl of milky nettle tea.",
"durability": null,
"name": "Nettle tea",
"weight": "0.9",
@ -39191,7 +39191,7 @@
"id": "4240"
},
{
"examine": "(In inventory) A handful of nettles (In ground) I better not get stung/I wouldn't like to get stung/I wish I could sting other people/Dock leaves at the ready/These may hurt/nettles sting my leggies(Draynor Village nettles)",
"examine": "A handful of nettles.",
"durability": null,
"name": "Nettles",
"archery_ticket_price": "0",
@ -39199,22 +39199,22 @@
},
{
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "A nice cup of nettle tea.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "4242"
},
{
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "A milky cup of nettle tea.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "4243"
@ -39228,22 +39228,22 @@
},
{
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "Some nettle tea in a porcelain cup.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "4245"
},
{
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "Some milky nettle tea in a porcelain cup.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "4246"
@ -70083,517 +70083,584 @@
"id": "7687"
},
{
"bankable": "false",
"examine": "The kettle is empty.",
"durability": null,
"name": "Kettle",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7688"
},
{
"bankable": "false",
"durability": null,
"name": "Kettle",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7689"
},
{
"bankable": "false",
"examine": "It's full of cold water.",
"durability": null,
"name": "Full kettle",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7690"
},
{
"bankable": "false",
"examine": "It's full of boiling water.",
"durability": null,
"name": "Hot kettle",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7691"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (4)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7692"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (4)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7693"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (3)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7694"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (3)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7695"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (2)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7696"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (2)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7697"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (1)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7698"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (1)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7699"
},
{
"bankable": "false",
"examine": "Add boiling water to make a tea.",
"durability": null,
"name": "Teapot with leaves",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7700"
},
{
"bankable": "false",
"durability": null,
"name": "Teapot with leaves",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7701"
},
{
"bankable": "false",
"examine": "This teapot is empty.",
"durability": null,
"name": "Teapot",
"tradeable": "true",
"tradeable": "false",
"weight": "0.4",
"archery_ticket_price": "0",
"id": "7702"
},
{
"bankable": "false",
"durability": null,
"name": "Teapot",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7703"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (4)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7704"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (4)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7705"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (3)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7706"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (3)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7707"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (2)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7708"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (2)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7709"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (1)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7710"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (1)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7711"
},
{
"bankable": "false",
"examine": "Add boiling water to make a tea.",
"durability": null,
"name": "Teapot with leaves",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7712"
},
{
"bankable": "false",
"durability": null,
"name": "Teapot with leaves",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7713"
},
{
"bankable": "false",
"examine": "This teapot is empty.",
"durability": null,
"name": "Teapot",
"tradeable": "true",
"tradeable": "false",
"weight": "0.4",
"archery_ticket_price": "0",
"id": "7714"
},
{
"bankable": "false",
"durability": null,
"name": "Teapot",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7715"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (4)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7716"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (4)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7717"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (3)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7718"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (3)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7719"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (2)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7720"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (2)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7721"
},
{
"bankable": "false",
"examine": "I'd really like a nice cup of tea.",
"durability": null,
"name": "Pot of tea (1)",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7722"
},
{
"bankable": "false",
"durability": null,
"name": "Pot of tea (1)",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7723"
},
{
"bankable": "false",
"examine": "Add boiling water to make a tea.",
"durability": null,
"name": "Teapot with leaves",
"tradeable": "true",
"tradeable": "false",
"weight": "1.5",
"archery_ticket_price": "0",
"id": "7724"
},
{
"bankable": "false",
"durability": null,
"name": "Teapot with leaves",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7725"
},
{
"bankable": "false",
"examine": "This teapot is empty.",
"durability": null,
"name": "Teapot",
"tradeable": "true",
"tradeable": "false",
"weight": "0.4",
"archery_ticket_price": "0",
"id": "7726"
},
{
"bankable": "false",
"durability": null,
"name": "Teapot",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7727"
},
{
"bankable": "false",
"shop_price": "1",
"examine": "An empty cup.",
"grand_exchange_price": "7",
"durability": null,
"name": "Empty cup",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7728"
},
{
"bankable": "false",
"durability": null,
"name": "Empty cup",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7729"
},
{
"bankable": "false",
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "A nice cup of nettle tea.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "7730"
},
{
"bankable": "false",
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "A milky cup of nettle tea.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "7731"
},
{
"bankable": "false",
"examine": "A porcelain cup.",
"durability": null,
"name": "Porcelain cup",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7732"
},
{
"bankable": "false",
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "Some nettle tea in a porcelain cup.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "7733"
},
{
"bankable": "false",
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "Some milky nettle tea in a porcelain cup.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "7734"
},
{
"bankable": "false",
"examine": "A porcelain cup.",
"durability": null,
"name": "Porcelain cup",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7735"
},
{
"bankable": "false",
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "Some nettle tea in a porcelain cup.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "7736"
},
{
"bankable": "false",
"shop_price": "10",
"examine": "A nice cup of tea.",
"examine": "Some milky nettle tea in a porcelain cup.",
"grand_exchange_price": "30",
"durability": null,
"name": "Cup of tea",
"tradeable": "true",
"tradeable": "false",
"weight": "0.1",
"archery_ticket_price": "0",
"id": "7737"
},
{
"bankable": "false",
"examine": "Mmm, how about a nice cup of tea?",
"durability": null,
"name": "Tea leaves",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7738"
},
{
"bankable": "false",
"durability": null,
"name": "Tea leaves",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7739"
},
{
"bankable": "false",
"examine": "A glass of frothy ale.",
"grand_exchange_price": "151",
"durability": null,
"name": "Beer",
"tradeable": "true",
"tradeable": "false",
"weight": "1",
"archery_ticket_price": "0",
"id": "7740"
},
{
"bankable": "false",
"durability": null,
"name": "Beer",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7741"
},
{
"bankable": "false",
"shop_price": "1",
"examine": "I need to fill this with beer.",
"grand_exchange_price": "25",
"durability": null,
"name": "Beer glass",
"tradeable": "true",
"tradeable": "false",
"weight": "0.05",
"archery_ticket_price": "0",
"id": "7742"
},
{
"bankable": "false",
"durability": null,
"name": "Beer glass",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7743"
},
{
"bankable": "false",
"shop_price": "3",
"examine": "Probably the finest readily-available ale in Asgarnia.",
"grand_exchange_price": "131",
"durability": null,
"name": "Asgarnian ale",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7744"
},
{
"bankable": "false",
"durability": null,
"name": "Asgarnian ale",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7745"
},
{
"bankable": "false",
"shop_price": "2",
"examine": "A glass of frothy ale.",
"grand_exchange_price": "569",
"durability": null,
"name": "Greenman's ale",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7746"
},
{
"bankable": "false",
"durability": null,
"name": "Greenman's ale",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7747"
},
{
"bankable": "false",
"shop_price": "2",
"examine": "A glass of bitter.",
"grand_exchange_price": "523",
"durability": null,
"name": "Dragon bitter",
"tradeable": "true",
"tradeable": "false",
"weight": "1",
"archery_ticket_price": "0",
"id": "7748"
},
{
"bankable": "false",
"durability": null,
"name": "Dragon bitter",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7749"
},
@ -70615,36 +70682,40 @@
"id": "7751"
},
{
"bankable": "false",
"shop_price": "2",
"examine": "A glass of Cider",
"grand_exchange_price": "1539",
"durability": null,
"name": "Cider",
"tradeable": "true",
"tradeable": "false",
"weight": "1",
"archery_ticket_price": "0",
"id": "7752"
},
{
"bankable": "false",
"durability": null,
"name": "Cider",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7753"
},
{
"bankable": "false",
"examine": "A fruity, full-bodied ale.",
"grand_exchange_price": "2371",
"durability": null,
"name": "Chef's delight",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7754"
},
{
"bankable": "false",
"durability": null,
"name": "Chef's delight",
"tradeable": "true",
"tradeable": "false",
"archery_ticket_price": "0",
"id": "7755"
},

View file

@ -1175,7 +1175,7 @@
"attack_level": "1"
},
{
"examine": "A barely visible deadly jungle spider.",
"examine": "A barely visible deadly spider.",
"melee_animation": "5327",
"range_animation": "5327",
"combat_audio": "537,539,538",
@ -2949,7 +2949,7 @@
"attack_level": "30"
},
{
"examine": "One of Gielinor's many citizens",
"examine": "One of Gielinor's many citizens.",
"melee_animation": "422",
"range_animation": "0",
"combat_audio": "511,513,512",
@ -3177,6 +3177,7 @@
"attack_level": "1"
},
{
"examine": "This one's had too much to drink!",
"melee_animation": "422",
"combat_audio": "703,705,704",
"respawn_delay": "60",
@ -6227,6 +6228,22 @@
"range_level": "1",
"attack_level": "1"
},
{
"examine": "A man, learned in the ways of the stars.",
"melee_animation": "0",
"range_animation": "0",
"defence_animation": "0",
"magic_animation": "0",
"death_animation": "0",
"name": "Observatory professor",
"defence_level": "1",
"safespot": null,
"lifepoints": "10",
"strength_level": "1",
"id": "488",
"range_level": "1",
"attack_level": "1"
},
{
"examine": "He doesn't look like he'd trust his own mother.",
"melee_animation": "6199",
@ -6237,13 +6254,13 @@
"magic_animation": "0",
"death_animation": "6190",
"name": "Goblin guard",
"defence_level": "20",
"defence_level": "37",
"safespot": null,
"lifepoints": "28",
"strength_level": "20",
"lifepoints": "43",
"strength_level": "37",
"id": "489",
"range_level": "1",
"attack_level": "20"
"attack_level": "32"
},
{
"examine": "If the mummy is at school",
@ -8565,7 +8582,7 @@
"attack_level": "1"
},
{
"examine": "Smartly dressed",
"examine": "Smartly dressed, and ready to deliver food.",
"melee_animation": "0",
"range_animation": "0",
"defence_animation": "0",
@ -9414,7 +9431,7 @@
"attack_level": "15"
},
{
"examine": "Children are just like real people...just smaller.",
"examine": "A sad looking child.",
"melee_animation": "0",
"range_animation": "0",
"defence_animation": "0",
@ -10518,13 +10535,13 @@
"death_animation": "5329",
"name": "Poison spider",
"safespot": null,
"defence_level": "52",
"lifepoints": "64",
"strength_level": "65",
"defence_level": "28",
"lifepoints": "25",
"strength_level": "28",
"id": "1009",
"aggressive": "true",
"range_level": "1",
"attack_level": "50"
"attack_level": "28"
},
{
"examine": "A green skinned croaker",
@ -16891,7 +16908,7 @@
"lifepoints": "120",
"strength_level": "45",
"id": "1618",
"aggressive": "true",
"aggressive": "false",
"clue_level": "2",
"range_level": "1",
"attack_level": "75"
@ -25079,42 +25096,52 @@
"melee_animation": "275",
"range_animation": "0",
"combat_audio": "3609,3608,3610",
"attack_speed": "7",
"poisonous": "true",
"magic_level": "1",
"respawn_delay": "60",
"defence_animation": "0",
"defence_animation": "276",
"weakness": "1",
"poison_amount": "55",
"magic_animation": "0",
"death_animation": "278",
"name": "Bush snake",
"defence_level": "61",
"defence_level": "50",
"safespot": null,
"lifepoints": "87",
"strength_level": "1",
"lifepoints": "25",
"strength_level": "25",
"id": "2489",
"aggressive": "true",
"range_level": "61",
"attack_level": "1"
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "1",
"attack_level": "25"
},
{
"examine": "A slithering serpent that likes to hide in the bush.",
"melee_animation": "275",
"combat_audio": "3609,3608,3610",
"range_animation": "0",
"attack_speed": "7",
"poisonous": "true",
"magic_level": "1",
"respawn_delay": "60",
"defence_animation": "276",
"poison_amount": "55",
"magic_animation": "0",
"death_animation": "278",
"name": "Bush snake",
"defence_level": "1",
"defence_level": "50",
"safespot": null,
"lifepoints": "25",
"strength_level": "1",
"strength_level": "25",
"id": "2490",
"aggressive": "true",
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "1",
"attack_level": "1"
"attack_level": "25"
},
{
"examine": "",
"melee_animation": "5327",
"combat_audio": "537,539,538",
"respawn_delay": "60",
@ -30854,6 +30881,7 @@
"attack_level": "1"
},
{
"examine": "One of Gielinor's many citizens.",
"name": "Woman",
"defence_level": "1",
"safespot": null,
@ -36003,7 +36031,7 @@
"name": "Tortoise",
"defence_level": "36",
"safespot": null,
"lifepoints": "51",
"lifepoints": "121",
"strength_level": "36",
"id": "3808",
"range_level": "1",
@ -36077,61 +36105,68 @@
"examine": "A Gnome Arrow-chucker",
"combat_style": "1",
"melee_animation": "190",
"range_animation": "0",
"range_animation": "190",
"respawn_delay": "60",
"defence_animation": "0",
"defence_animation": "193",
"weakness": "2",
"magic_animation": "0",
"death_animation": "196",
"name": "Gnome Archer",
"defence_level": "30",
"defence_level": "1",
"safespot": null,
"lifepoints": "42",
"lifepoints": "10",
"strength_level": "1",
"id": "3814",
"aggressive": "true",
"range_level": "30",
"attack_level": "1"
"range_level": "5",
"projectile": "10",
"attack_level": "1",
"prj_height": "30"
},
{
"examine": "Yee haa!",
"melee_animation": "3969",
"range_animation": "0",
"respawn_delay": "60",
"defence_animation": "0",
"defence_animation": "193",
"weakness": "9",
"magic_animation": "0",
"death_animation": "196",
"name": "Gnome Driver",
"defence_level": "33",
"defence_level": "1",
"safespot": null,
"lifepoints": "47",
"strength_level": "33",
"lifepoints": "10",
"strength_level": "1",
"id": "3815",
"aggressive": "true",
"range_level": "1",
"attack_level": "33"
"attack_level": "5"
},
{
"examine": "A battle mage of the gnomish variety.",
"combat_style": "2",
"start_gfx": "93",
"start_height": "80",
"melee_animation": "3968",
"range_animation": "0",
"magic_level": "34",
"magic_level": "5",
"spell_id": "",
"respawn_delay": "60",
"defence_animation": "0",
"defence_animation": "193",
"weakness": "5",
"magic_animation": "0",
"magic_animation": "200",
"death_animation": "196",
"name": "Gnome Mage",
"defence_level": "34",
"defence_level": "1",
"safespot": null,
"lifepoints": "48",
"lifepoints": "10",
"strength_level": "1",
"id": "3816",
"aggressive": "true",
"range_level": "1",
"attack_level": "1"
"projectile": "94",
"attack_level": "1",
"prj_height": "30"
},
{
"examine": "The cruel tortoise trainer. Boo!",
@ -36160,7 +36195,7 @@
"name": "Tortoise",
"defence_level": "36",
"safespot": null,
"lifepoints": "51",
"lifepoints": "101",
"strength_level": "36",
"id": "3819",
"range_level": "1",
@ -37498,15 +37533,30 @@
"attack_level": "1"
},
{
"name": "Snake",
"defence_level": "1",
"safespot": null,
"lifepoints": "10",
"examine": "Snake! Snaaaaake! ",
"combat_audio": "3609,3608,3610",
"strength_level": "1",
"range_animation": "0",
"melee_animation": "275",
"attack_speed": "3",
"poisonous": "true",
"magic_level": "1",
"end_gfx": "",
"respawn_delay": "30",
"defence_animation": "276",
"poison_amount": "15",
"end_height": "",
"magic_animation": "0",
"death_animation": "278",
"name": "Snake",
"defence_level": "50",
"safespot": null,
"lifepoints": "25",
"strength_level": "25",
"id": "4343",
"aggressive": "true",
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "1",
"attack_level": "1"
"attack_level": "25"
},
{
"melee_animation": "220",
@ -53269,6 +53319,7 @@
"attack_level": "1"
},
{
"examine": "An ugly green creature.",
"name": "Goblin",
"defence_level": "1",
"safespot": null,
@ -56220,6 +56271,7 @@
"attack_level": "48"
},
{
"examine": "This one's had too much to drink!",
"melee_animation": "390",
"combat_audio": "703,705,704",
"respawn_delay": "60",
@ -56236,6 +56288,7 @@
"attack_level": "1"
},
{
"examine": "This one's had too much to drink!",
"melee_animation": "390",
"combat_audio": "703,705,704",
"respawn_delay": "60",
@ -56252,6 +56305,7 @@
"attack_level": "1"
},
{
"examine": "This one's had too much to drink!",
"melee_animation": "390",
"combat_audio": "703,705,704",
"respawn_delay": "60",
@ -56268,6 +56322,7 @@
"attack_level": "1"
},
{
"examine": "This one's had too much to drink!",
"melee_animation": "390",
"combat_audio": "703,705,704",
"respawn_delay": "60",
@ -56284,6 +56339,7 @@
"attack_level": "1"
},
{
"examine": "This one's had too much to drink!",
"melee_animation": "390",
"combat_audio": "703,705,704",
"respawn_delay": "60",
@ -56610,6 +56666,7 @@
"attack_level": "1"
},
{
"examine": "A barely visible deadly spider.",
"melee_animation": "5327",
"combat_audio": "537,539,538",
"respawn_delay": "60",
@ -56626,6 +56683,7 @@
"attack_level": "1"
},
{
"examine": "A barely visible deadly spider.",
"melee_animation": "5327",
"combat_audio": "537,539,538",
"respawn_delay": "60",
@ -56737,6 +56795,7 @@
"attack_level": "45"
},
{
"examine": "That stinger has to hurt...",
"melee_animation": "7242",
"attack_speed": "5",
"respawn_delay": "60",
@ -63370,7 +63429,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny",
"examine": "It's a tiny, cheeky monkey.",
"melee_animation": "0",
"range_animation": "0",
"combat_audio": "629,631,630",
@ -65394,7 +65453,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny",
"examine": "It's a tiny, cheeky monkey.",
"melee_animation": "0",
"range_animation": "0",
"combat_audio": "629,631,630",
@ -65411,7 +65470,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny",
"examine": "It's a tiny, cheeky monkey.",
"melee_animation": "0",
"range_animation": "0",
"combat_audio": "629,631,630",
@ -65428,7 +65487,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny",
"examine": "It's a tiny, cheeky monkey.",
"melee_animation": "0",
"range_animation": "0",
"combat_audio": "629,631,630",
@ -65445,7 +65504,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny",
"examine": "It's a tiny, cheeky monkey.",
"melee_animation": "0",
"range_animation": "0",
"combat_audio": "629,631,630",
@ -65462,7 +65521,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny",
"examine": "It's a tiny, cheeky monkey.",
"melee_animation": "0",
"range_animation": "0",
"combat_audio": "629,631,630",
@ -65479,7 +65538,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny",
"examine": "It's a tiny, cheeky monkey.",
"melee_animation": "0",
"range_animation": "0",
"combat_audio": "629,631,630",
@ -65496,6 +65555,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny, cheeky monkey.",
"name": "Baby monkey",
"defence_level": "1",
"safespot": null,
@ -65507,6 +65567,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny, cheeky monkey.",
"name": "Baby monkey",
"defence_level": "1",
"safespot": null,
@ -65518,6 +65579,7 @@
"attack_level": "1"
},
{
"examine": "It's a tiny, cheeky monkey.",
"name": "Baby monkey",
"defence_level": "1",
"safespot": null,
@ -72684,9 +72746,10 @@
{
"examine": "Rock with attitude.",
"combat_style": "1",
"range_animation": "0",
"attack_speed": "4",
"defence_animation": "8",
"range_animation": "5347",
"attack_speed": "6",
"defence_animation": "154",
"death_animation": "156",
"name": "Rock Golem",
"lifepoints": "25",
"strength_level": "8",
@ -72694,14 +72757,16 @@
"aggressive": "true",
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "15",
"projectile": "968",
"attack_level": "8"
},
{
"examine": "Rock with attitude.",
"combat_style": "1",
"range_animation": "0",
"attack_speed": "4",
"defence_animation": "17",
"range_animation": "5347",
"attack_speed": "6",
"defence_animation": "154",
"death_animation": "156",
"name": "Rock Golem",
"lifepoints": "40",
"strength_level": "17",
@ -72709,14 +72774,16 @@
"aggressive": "true",
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "30",
"projectile": "968",
"attack_level": "17"
},
{
"examine": "Rock with attitude.",
"combat_style": "1",
"range_animation": "0",
"attack_speed": "4",
"defence_animation": "36",
"range_animation": "5347",
"attack_speed": "6",
"defence_animation": "154",
"death_animation": "156",
"name": "Rock Golem",
"lifepoints": "60",
"strength_level": "36",
@ -72724,14 +72791,16 @@
"aggressive": "true",
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "40",
"projectile": "968",
"attack_level": "38"
},
{
"examine": "Rock with attitude.",
"combat_style": "1",
"range_animation": "0",
"attack_speed": "4",
"defence_animation": "64",
"range_animation": "5347",
"attack_speed": "6",
"defence_animation": "154",
"death_animation": "156",
"name": "Rock Golem",
"lifepoints": "86",
"strength_level": "64",
@ -72739,14 +72808,16 @@
"aggressive": "true",
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "60",
"projectile": "968",
"attack_level": "64"
},
{
"examine": "Rock with attitude.",
"combat_style": "1",
"range_animation": "0",
"attack_speed": "4",
"defence_animation": "100",
"range_animation": "5347",
"attack_speed": "6",
"defence_animation": "154",
"death_animation": "156",
"name": "Rock Golem",
"lifepoints": "120",
"strength_level": "100",
@ -72754,14 +72825,16 @@
"aggressive": "true",
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "75",
"projectile": "968",
"attack_level": "100"
},
{
"examine": "Rock with attitude.",
"combat_style": "1",
"range_animation": "0",
"attack_speed": "4",
"defence_animation": "130",
"range_animation": "5347",
"attack_speed": "6",
"defence_animation": "154",
"death_animation": "156",
"name": "Rock Golem",
"lifepoints": "166",
"strength_level": "130",
@ -72769,6 +72842,7 @@
"aggressive": "true",
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"range_level": "80",
"projectile": "968",
"attack_level": "130"
},
{
@ -73422,6 +73496,7 @@
"id": "366"
},
{
"examine": "Owner of the banana plantation.",
"name": "Luthas",
"id": "379"
},
@ -73530,22 +73605,22 @@
"id": "486"
},
{
"name": "Observatory professor",
"id": "488"
},
{
"examine": "A cart driver; it looks like he's quite experienced.",
"name": "Hajedy",
"id": "510"
},
{
"examine": "A cart driver; it looks like he's quite experienced.",
"name": "Vigroy",
"id": "511"
},
{
"examine": "This is Yohnus - he runs the local blacksmiths.",
"name": "Yohnus",
"id": "513"
},
{
"examine": "This is Seravel. He sells tickets for the 'Lady of the Waves.'",
"name": "Seravel",
"id": "514"
},
@ -73562,6 +73637,7 @@
"id": "553"
},
{
"examine": "Goods for sale and trade.",
"name": "Jiminua",
"id": "560"
},
@ -73574,6 +73650,7 @@
"id": "565"
},
{
"examine": "Appears slightly drunk.",
"name": "Zambo",
"id": "568"
},
@ -73598,6 +73675,7 @@
"id": "585"
},
{
"examine": "An amulet trader.",
"name": "Davon",
"id": "588"
},
@ -73766,6 +73844,7 @@
"id": "673"
},
{
"examine": "Builds ships for a living.",
"name": "Shipyard worker",
"id": "675"
},
@ -73854,6 +73933,7 @@
"id": "725"
},
{
"examine": "A wise old witch doctor.",
"name": "Trufitus",
"id": "740"
},
@ -73954,10 +74034,12 @@
"id": "784"
},
{
"examine": "A diligent guard.",
"name": "Garv",
"id": "788"
},
{
"examine": "A rough looking thief.",
"name": "Grubor",
"id": "789"
},
@ -73966,6 +74048,7 @@
"id": "791"
},
{
"examine": "Looks like he's been in the wars.",
"name": "Grip",
"id": "792"
},
@ -74061,6 +74144,7 @@
"id": "844"
},
{
"examine": "A happening kind of guy!",
"name": "Kangai Mau",
"id": "846"
},
@ -74337,14 +74421,17 @@
"id": "1159"
},
{
"examine": "The imposing Chief of Tai Bwo Wannai.",
"name": "Timfraku",
"id": "1162"
},
{
"examine": "A rather depressed looking fisherman.",
"name": "Tiadeche",
"id": "1163"
},
{
"examine": "A rather depressed looking fisherman.",
"name": "Tiadeche",
"id": "1164"
},
@ -74357,6 +74444,7 @@
"id": "1166"
},
{
"examine": "A hunter who long ago succumbed to blood lust.",
"name": "Tamayu",
"id": "1167"
},
@ -74373,6 +74461,7 @@
"id": "1170"
},
{
"examine": "A crotchety old fisherman who doesn't like young whippersnappers.",
"name": "Lubufu",
"id": "1171"
},
@ -74397,10 +74486,12 @@
"id": "1178"
},
{
"examine": "A sea bird.",
"name": "Cormorant",
"id": "1180"
},
{
"examine": "A sea bird.",
"name": "Pelican",
"id": "1181"
},
@ -74766,6 +74857,7 @@
},
{
"name": "Solihib",
"movement_radius": "1",
"id": "1433"
},
{
@ -75069,6 +75161,7 @@
"id": "1581"
},
{
"examine": "Looks like he wants money.",
"name": "Saniboch",
"id": "1595"
},
@ -75413,6 +75506,7 @@
"id": "1883"
},
{
"examine": "A well dressed thief.",
"name": "Trobert",
"id": "1884"
},
@ -76569,26 +76663,32 @@
"id": "2590"
},
{
"examine": "Another one of those mystic-types.",
"name": "TzHaar-Mej-Jal",
"id": "2617"
},
{
"examine": "Another one of those mystic-types.",
"name": "TzHaar-Mej-Kah",
"id": "2618"
},
{
"examine": "Maybe it'll guard my possessions.",
"name": "TzHaar-Ket-Zuh",
"id": "2619"
},
{
"examine": "Wonder what it's making.",
"name": "TzHaar-Hur-Tel",
"id": "2620"
},
{
"examine": "Wonder what it's making.",
"name": "TzHaar-Hur-Lek",
"id": "2622"
},
{
"examine": "Another one of those mystic-types.",
"name": "TzHaar-Mej-Roh",
"id": "2623"
},
@ -80464,6 +80564,7 @@
"id": "4577"
},
{
"examine": "He looks like he's a long way from home.",
"name": "Penwie",
"id": "4578"
},
@ -82834,6 +82935,7 @@
"id": "6114"
},
{
"examine": "A man, learned in the ways of the stars.",
"name": "Observatory professor",
"id": "6119"
},
@ -83534,10 +83636,12 @@
"id": "6915"
},
{
"examine": "",
"name": "Gecko",
"id": "6916"
},
{
"examine": "How cute!",
"name": "Gecko",
"id": "6918"
},
@ -84126,6 +84230,7 @@
"id": "7280"
},
{
"examine": "",
"name": "Gecko",
"id": "7281"
},
@ -84142,14 +84247,17 @@
"id": "7284"
},
{
"examine": "It's so adorably tiny!",
"name": "Baby gecko",
"id": "7286"
},
{
"examine": "",
"name": "Baby gecko",
"id": "7287"
},
{
"examine": "It's so adorably tiny!",
"name": "Baby gecko",
"id": "7288"
},
@ -84158,6 +84266,7 @@
"id": "7289"
},
{
"examine": "How cute!",
"name": "Gecko",
"id": "7290"
},
@ -84166,6 +84275,7 @@
"id": "7291"
},
{
"examine": "How cute!",
"name": "Gecko",
"id": "7292"
},
@ -86918,6 +87028,7 @@
"id": "8274"
},
{
"examine": "He looks dangerous!",
"name": "Duradel",
"id": "8275"
},
@ -88072,5 +88183,10 @@
"examine": "He looks angry and smells drunk.",
"name": "Skippy",
"id": "2796"
},
{
"examine": "Angry and stressed, he doesn't seem to have time for anything other than sand.",
"name": "Sandy",
"id": "3112"
}
]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -201,6 +201,7 @@ public enum Consumables {
/** Ales */
ASGOLDIAN_ALE(new FakeConsumable(7508, new String[] {"I don't think I'd like gold in beer thanks. Leave it for the dwarves."})),
ASGARNIAN_ALE(new Drink(new int[] {1905, 1919}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
ASGARNIAN_ALE_POH(new Drink(new int[] {7744, 7742}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
ASGARNIAN_ALE_KEG(new Drink(new int[] {5785, 5783, 5781, 5779, 5769}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), new Animation(2289), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
ASGARNIAN_ALE_M(new Drink(new int[] {5739, 1919}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)))),
ASGARNIAN_ALE_M_KEG(new Drink(new int[] {5865, 5863, 5861, 5859, 5769}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), new Animation(2289))),
@ -210,17 +211,21 @@ public enum Consumables {
AXEMANS_FOLLY_M_KEG(new Drink(new int[] {5905, 5903, 5901, 5899, 5769}, new MultiEffect(new SkillEffect(Skills.WOODCUTTING, 2, 0), new HealingEffect(2), new SkillEffect(Skills.STRENGTH, -4, 0), new SkillEffect(Skills.ATTACK, -4, 0)), new Animation(2289))),
BANDITS_BREW(new Drink(new int[] {4627, 1919}, new MultiEffect(new SkillEffect(Skills.THIEVING, 1, 0), new SkillEffect(Skills.ATTACK, 1, 0), new SkillEffect(Skills.STRENGTH, -1, 0), new SkillEffect(Skills.DEFENCE, -6, 0), new HealingEffect(1)), "You drink the beer. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
BEER(new Drink(new int[] {1917, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 0, 0.04), new SkillEffect(Skills.ATTACK, 0, -0.07)), "You drink the beer. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
BEER_POH(new Drink(new int[] {7740, 7742}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 0, 0.04), new SkillEffect(Skills.ATTACK, 0, -0.07)), "You drink the beer. You feel slightly reinvigorated...", "...and slightly dizzy too.")),
BEER_TANKARD(new Drink(new int[] {3803, 3805}, new MultiEffect(new SkillEffect(Skills.ATTACK, -9, 0), new SkillEffect(Skills.STRENGTH, 4, 0)), "You quaff the beer. You feel slightly reinvigorated...", "...but very dizzy too.")),
KEG_OF_BEER(new Drink(new int[] {3801}, new KegOfBeerEffect(), new Animation(1330), "You chug the keg. You feel reinvigorated...", "...but extremely drunk too.")),
CHEFS_DELIGHT(new Drink(new int[] {5755, 1919}, new MultiEffect(new SkillEffect(Skills.COOKING, 1, 0.05), new HealingEffect(1), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))),
CHEFS_DELIGHT_POH(new Drink(new int[] {7754, 7742}, new MultiEffect(new SkillEffect(Skills.COOKING, 1, 0.05), new HealingEffect(1), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))),
CHEFS_DELIGHT_KEG(new Drink(new int[] {5833, 5831, 5829, 5827, 5769}, new MultiEffect(new SkillEffect(Skills.COOKING, 1, 0.05), new HealingEffect(1), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))),
CHEFS_DELIGHT_M(new Drink(new int[] {5757, 1919}, new MultiEffect(new SkillEffect(Skills.COOKING, 2, 0.05), new HealingEffect(2), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0)))),
CHEFS_DELIGHT_M_KEG(new Drink(new int[] {5913, 5911, 5909, 5907, 5769}, new MultiEffect(new SkillEffect(Skills.COOKING, 2, 0.05), new HealingEffect(2), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0)), new Animation(2289))),
CIDER(new Drink(new int[] {5763, 1919}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 1, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0))))),
CIDER_POH(new Drink(new int[] {7752, 7742}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 1, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0))))),
CIDER_KEG(new Drink(new int[] {5849, 5847, 5845, 5843, 5769}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 1, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0))), new Animation(2289))),
MATURE_CIDER(new Drink(new int[] {5765, 1919}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 2, 0), new SkillEffect(Skills.ATTACK, -5, 0), new SkillEffect(Skills.STRENGTH, -5, 0))))),
CIDER_M_KEG(new Drink(new int[] {5929, 5927, 5925, 5923, 5769}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 2, 0), new SkillEffect(Skills.ATTACK, -5, 0), new SkillEffect(Skills.STRENGTH, -5, 0))), new Animation(2289))),
DRAGON_BITTER(new Drink(new int[] {1911, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)))),
DRAGON_BITTER_POH(new Drink(new int[] {7748, 7742}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)))),
DRAGON_BITTER_KEG(new Drink(new int[] {5809, 5807, 5805, 5803, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), new Animation(2289))),
DRAGON_BITTER_M(new Drink(new int[] {5745, 1919}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)))),
DRAGON_BITTER_M_KEG(new Drink(new int[] {5889, 5887, 5885, 5883, 5769}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), new Animation(2289))),
@ -229,6 +234,7 @@ public enum Consumables {
DWARVEN_STOUT_M(new Drink(new int[] {5747, 1919}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)))),
DWARVEN_STOUT_M_KEG(new Drink(new int[] {5857, 5855, 5853, 5851, 5769}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)), new Animation(2289))),
GREENMANS_ALE(new Drink(new int[] {1909, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)))),
GREENMANS_ALE_POH(new Drink(new int[] {7746, 7742}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)))),
GREENMANS_ALE_KEG(new Drink(new int[] {5793, 5791, 5789, 5787, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)), new Animation(2289))),
GREENMANS_ALE_M(new Drink(new int[] {5743, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))),
GREENMANS_ALE_M_KEG(new Drink(new int[] {5873, 5871, 5869, 5867, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))),
@ -285,16 +291,19 @@ public enum Consumables {
/** Tea */
CUP_OF_TEA(new Drink(new int[] {712, 1980}, new MultiEffect(new HealingEffect(3), new SkillEffect(Skills.ATTACK, 3, 0)), "Aaah, nothing like a nice cuppa tea!")),
CUP_OF_TEA_NETTLE(new Drink(new int[] {4242, 1980}, new EnergyEffect(10))),
CUP_OF_TEA_MILKY_NETTLE(new Drink(new int[] {4243, 1980}, new EnergyEffect(10))),
NETTLE_TEA(new Drink(new int[] {4239, 1923}, new NettleTeaEffect())),
NETTLE_TEA_MILKY(new Drink(new int[] {4240, 1980}, new NettleTeaEffect())),
NETTLE_WATER(new Drink(new int[] {Items.NETTLE_WATER_4237, Items.BOWL_1923}, new HealingEffect((1)))),
CUP_OF_TEA_NETTLE(new Drink(new int[] {4242, 1980}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))),
CUP_OF_TEA_MILKY_NETTLE(new Drink(new int[] {4243, 1980}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))),
CUP_OF_TEA_NETTLE_PORCELAIN(new Drink(new int[] {4245, 4244}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))),
CUP_OF_TEA_MILKY_NETTLE_PORCELAIN(new Drink(new int[] {4246, 4244}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))),
NETTLE_TEA(new Drink(new int[] {4239, 1923}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))),
NETTLE_TEA_MILKY(new Drink(new int[] {4240, 1923}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))),
CUP_OF_TEA_CLAY(new Drink(new int[] {7730, 7728}, new SkillEffect(Skills.CONSTRUCTION, 1, 0), "You feel refreshed and ready for more building.")),
CUP_OF_TEA_CLAY_MILKY(new Drink(new int[] {7731, 7728}, new SkillEffect(Skills.CONSTRUCTION, 1, 0))),
CUP_OF_TEA_CLAY_MILKY(new Drink(new int[] {7731, 7728}, new SkillEffect(Skills.CONSTRUCTION, 1, 0), "You feel refreshed and ready for more building.")),
CUP_OF_TEA_WHITE(new Drink(new int[] {7733, 7732}, new SkillEffect(Skills.CONSTRUCTION, 2, 0), "You feel refreshed and ready for more building.")),
CUP_OF_TEA_WHITE_MILKY(new Drink(new int[] {7734, 7732}, new SkillEffect(Skills.CONSTRUCTION, 2, 0))),
CUP_OF_TEA_WHITE_MILKY(new Drink(new int[] {7734, 7732}, new SkillEffect(Skills.CONSTRUCTION, 2, 0), "You feel refreshed and ready for more building.")),
CUP_OF_TEA_GOLD(new Drink(new int[] {7736, 7735}, new SkillEffect(Skills.CONSTRUCTION, 3, 0), "You feel refreshed and ready for more building.")),
CUP_OF_TEA_GOLD_MILKY(new Drink(new int[] {7737, 7735}, new SkillEffect(Skills.CONSTRUCTION, 3, 0))),
CUP_OF_TEA_GOLD_MILKY(new Drink(new int[] {7737, 7735}, new SkillEffect(Skills.CONSTRUCTION, 3, 0), "You feel refreshed and ready for more building.")),
/** Miscellaneous */
CHOCOLATE_BAR(new Food(new int[] {1973}, new HealingEffect(3))),

View file

@ -1,20 +0,0 @@
package content.data.consumables.effects;
import core.game.consumable.ConsumableEffect;
import core.game.node.entity.player.Player;
public class NettleTeaEffect extends ConsumableEffect {
private final static int healing = 3;
@Override
public void activate(Player p) {
final ConsumableEffect effect = p.getSkills().getLifepoints() < p.getSkills().getMaximumLifepoints() ? new MultiEffect(new HealingEffect(3), new EnergyEffect(5)) : new HealingEffect(3);
effect.activate(p);
}
@Override
public int getHealthEffectValue(Player player) {
return healing;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -133,16 +133,16 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com
}
override fun defineCommands() {
define("tostar", Privilege.ADMIN) { player, _ ->
define("tostar", Privilege.ADMIN, description = "Teleports you adjacent to the currently active star.") { player, _ ->
teleport(player, star.starObject.location.transform(1,1,0))
}
define("submit", Privilege.ADMIN) { _, _ ->
define("submit", Privilege.ADMIN, description = "Rebuilds and fires the shooting star event immediately.") { _, _ ->
star.rebuildVars()
star.fire()
}
define("resetsprite", Privilege.ADMIN) { player, _ ->
define("resetsprite", Privilege.ADMIN, description = "Resets your star sprite reward cooldown.") { player, _ ->
player.savedData.globalData.starSpriteDelay = 0L
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,47 +4,22 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.*
import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.component.Component.setUnclosable
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.TeleportManager
import core.game.system.timer.impl.AntiMacro
import core.game.world.map.Location
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
// "::revent [-p] <lt>player name<gt> [-e <lt>event name<gt>]"
class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) {
override fun init() {
super.init()
sendChat("${player.username}, you're under arrest!")
face(player)
player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
PilloryInterface.initPillory(player)
val dest = PilloryInterface.LOCATIONS.random() //9 random spots!
kidnapPlayer(player, dest, TeleportManager.TeleportType.INSTANT)
AntiMacro.terminateEventNpc(player)
sendGraphics(Graphics(1577, 0, 0), player.location)
animate(player,8941)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
kidnapPlayer(this, player, PilloryInterface.LOCATIONS.random()) { player, _ ->
PilloryInterface.initPillory(player)
setUnclosable(player, player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from."))
}
}
override fun talkTo(npc: NPC) {
//player.dialogueInterpreter.open(FreakyForesterDialogue(),npc)
sendMessage(player, "He isn't interested in talking to you.")
}
}

View file

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

View file

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

View file

@ -0,0 +1,86 @@
package content.global.ame.events.rockgolem
import core.game.node.entity.Entity
import core.game.node.entity.combat.BattleState
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.combat.CombatSwingHandler
import core.game.node.entity.combat.InteractionType
import core.game.node.entity.impl.Projectile
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
import core.game.world.update.flag.context.Animation
import org.rs09.consts.NPCs
class RockGolemBehavior() : NPCBehavior(NPCs.ROCK_GOLEM_413, NPCs.ROCK_GOLEM_414, NPCs.ROCK_GOLEM_415, NPCs.ROCK_GOLEM_416, NPCs.ROCK_GOLEM_417, NPCs.ROCK_GOLEM_418) {
private val swingHandler = RockGolemSwingHandler()
override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler {
return swingHandler
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return super.getXpMultiplier(self, attacker) / 16.0
}
}
private class RockGolemSwingHandler : CombatSwingHandler(CombatStyle.RANGE) {
override fun canSwing(entity: Entity, victim: Entity): InteractionType? {
return type?.swingHandler?.canSwing(entity, victim)
}
override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int {
if (entity == null || victim == null) return -1
val prayingRange = victim.hasProtectionPrayer(CombatStyle.RANGE)
type = if (prayingRange) {
CombatStyle.MELEE
} else {
CombatStyle.RANGE
}
if (type == CombatStyle.MELEE && entity.location.getDistance(victim.location) > 1) return -1
return type?.swingHandler?.swing(entity, victim, state) ?: 0
}
override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) {
type?.swingHandler?.impact(entity, victim, state)
}
override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) {
type?.swingHandler?.visualizeImpact(entity, victim, state)
}
override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) {
when (type) {
CombatStyle.MELEE -> {
entity.animate(Animation(153))
}
CombatStyle.RANGE -> {
val speed = (100 + (Projectile.getLocation(entity).getDistance(victim!!.getLocation()) * 5)).toInt()
Projectile.create(entity, victim, 968, 48, 36, 80, speed, 5, entity.size() shl 5).send()
entity.animate(Animation(5347))
}
else -> {}
}
}
override fun calculateAccuracy(entity: Entity?): Int {
return type?.swingHandler?.calculateAccuracy(entity) ?: 0
}
override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int {
return type?.swingHandler?.calculateHit(entity, victim, modifier) ?: 0
}
override fun calculateDefence(victim: Entity?, attacker: Entity?): Int {
return type?.swingHandler?.calculateDefence(victim, attacker) ?: 0
}
override fun getSetMultiplier(e: Entity?, skillId: Int): Double {
return type?.swingHandler?.getSetMultiplier(e, skillId) ?: 0.0
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -43,13 +43,39 @@ class EmptyOptionListener : InteractionListener {
BUCKET_OF_SLIME(Items.BUCKET_OF_SLIME_4286, Items.BUCKET_1925, "You empty the contents of the bucket on the floor.", Sounds.LIQUID_2401),
VIAL_OF_WATER(Items.VIAL_OF_WATER_227, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401),
BOWL_OF_WATER(Items.BOWL_OF_WATER_1921, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
BOWL_OF_HOT_WATER(Items.BOWL_OF_HOT_WATER_4456, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
JUG_OF_WATER(Items.JUG_OF_WATER_1937, Items.JUG_1935, "You empty the contents of the jug onto the floor.", Sounds.LIQUID_2401),
BURNT_PIE(Items.BURNT_PIE_2329, Items.PIE_DISH_2313, "You empty the pie dish."),
POTION(Items.POTION_195, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401),
BURNT_STEW(Items.BURNT_STEW_2005, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
NETTLE_TEA(Items.NETTLE_TEA_4239, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
CUP_OF_TEA(Items.CUP_OF_TEA_4242, Items.EMPTY_CUP_1980, "You empty the cup of tea.", Sounds.LIQUID_2401),
NETTLE_WATER(Items.NETTLE_WATER_4237, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
NETTLE_TEA_MILKY(Items.NETTLE_TEA_4240, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
NETTLE_TEA_CUP(Items.CUP_OF_TEA_4242, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
NETTLE_TEA_CUP_MILKY(Items.CUP_OF_TEA_4243, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
TEA_CUP_WATER(Items.CUP_OF_WATER_4458, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
TEA_CUP_HOT_WATER(Items.CUP_OF_HOT_WATER_4460, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
NETTLE_TEA_PORCELAIN(Items.CUP_OF_TEA_4245, Items.PORCELAIN_CUP_4244, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
NETTLE_TEA_PORCELAIN_MILKY(Items.CUP_OF_TEA_4246, Items.PORCELAIN_CUP_4244, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
BUILDERS_TEA1(Items.CUP_OF_TEA_7730, Items.EMPTY_CUP_7728, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
BUILDERS_TEA1_MILKY(Items.CUP_OF_TEA_7731, Items.EMPTY_CUP_7728, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
BUILDERS_TEA2(Items.CUP_OF_TEA_7733, Items.PORCELAIN_CUP_7732, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
BUILDERS_TEA2_MILKY(Items.CUP_OF_TEA_7734, Items.PORCELAIN_CUP_7732, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
BUILDERS_TEA3(Items.CUP_OF_TEA_7736, Items.PORCELAIN_CUP_7735, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
BUILDERS_TEA3_MILKY(Items.CUP_OF_TEA_7737, Items.PORCELAIN_CUP_7735, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
CLAY_POT_OF_TEA4(Items.POT_OF_TEA_4_7692, Items.TEAPOT_7702, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
CLAY_POT_OF_TEA3(Items.POT_OF_TEA_3_7694, Items.TEAPOT_7702, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
CLAY_POT_OF_TEA2(Items.POT_OF_TEA_2_7696, Items.TEAPOT_7702, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
CLAY_POT_OF_TEA1(Items.POT_OF_TEA_1_7698, Items.TEAPOT_7702, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
PORCELAIN_POT_OF_TEA4(Items.POT_OF_TEA_4_7704, Items.TEAPOT_7714, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
PORCELAIN_POT_OF_TEA3(Items.POT_OF_TEA_3_7706, Items.TEAPOT_7714, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
PORCELAIN_POT_OF_TEA2(Items.POT_OF_TEA_2_7708, Items.TEAPOT_7714, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
PORCELAIN_POT_OF_TEA1(Items.POT_OF_TEA_1_7710, Items.TEAPOT_7714, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
GOLD_PORCELAIN_POT_OF_TEA4(Items.POT_OF_TEA_4_7716, Items.TEAPOT_7726, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
GOLD_PORCELAIN_POT_OF_TEA3(Items.POT_OF_TEA_3_7718, Items.TEAPOT_7726, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
GOLD_PORCELAIN_POT_OF_TEA2(Items.POT_OF_TEA_2_7720, Items.TEAPOT_7726, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
GOLD_PORCELAIN_POT_OF_TEA1(Items.POT_OF_TEA_1_7722, Items.TEAPOT_7726, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401),
BURNT_CURRY(Items.BURNT_CURRY_2013, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
BURNT_GNOMEBOWL(Items.BURNT_GNOMEBOWL_2175, Items.GNOMEBOWL_MOULD_2166, "You empty the contents of the gnomebowl onto the floor."),
BURNT_EGG(Items.BURNT_EGG_7090, Items.BOWL_1923, "You empty the contents of the bowl onto the floor."),

View file

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

View file

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

View file

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

View file

@ -13,6 +13,7 @@ import core.game.world.map.RegionManager;
import core.game.world.update.flag.context.Graphics;
import core.tools.RandomFunction;
import java.util.ArrayList;
import java.util.List;
/**
@ -54,13 +55,15 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler {
entity.getProperties().getCombatPulse().stop();
return -1;
}
@SuppressWarnings("rawtypes")
List list = victim instanceof NPC ? RegionManager.getSurroundingNPCs(victim, 14, entity) : RegionManager.getSurroundingPlayers(victim, 14, entity);
BattleState[] targets = new BattleState[list.size()];
// ! Initial capacity of 14, but has dynamic size? i.e. we are not restricting to a max of 14 targets hit here now
List<Entity> targetCandidates = new ArrayList<>(14);
targetCandidates.addAll(RegionManager.getSurroundingNPCs(victim, 14, entity));
targetCandidates.addAll(RegionManager.getSurroundingPlayers(victim, 14, entity));
BattleState[] targets = new BattleState[targetCandidates.size()];
int count = 0;
for (Object o : list) {
Entity e = (Entity) o;
if (canSwing(entity, e) != InteractionType.NO_INTERACT) {
for (Entity e : targetCandidates) {
if (canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) {
BattleState s = targets[count++] = new BattleState(entity, e);
s.setStyle(CombatStyle.RANGE);
int hit = 0;

View file

@ -49,7 +49,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements
@Override
public void impact(Entity entity, Entity victim, BattleState state) {
if (state.getTargets() != null) {
if (state != null && state.getTargets() != null) {
for (BattleState s : state.getTargets()) {
if (s != null) {
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
@ -81,13 +81,13 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements
int count = 0;
for (Object o : list) {
Entity e = (Entity) o;
if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) {
if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) {
BattleState s = targets[count++] = new BattleState(entity, e);
int hit = 0;
if (isAccurateImpact(entity, e)) {
hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1);
}
s.setStyle(CombatStyle.MELEE);
s.setStyle(CombatStyle.MELEE);
s.setEstimatedHit(hit);
}
}
@ -103,7 +103,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements
@Override
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
if (state.getTargets() != null) {
if (state != null && state.getTargets() != null) {
for (BattleState s : state.getTargets()) {
if (s != null) {
s.getVictim().animate(victim.getProperties().getDefenceAnimation());

View file

@ -49,7 +49,7 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements
@Override
public void impact(Entity entity, Entity victim, BattleState state) {
if (state.getTargets() != null) {
if (state != null && state.getTargets() != null) {
for (BattleState s : state.getTargets()) {
if (s != null) {
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
@ -82,11 +82,11 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements
int count = 0;
for (Object o : list) {
Entity e = (Entity) o;
if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) {
if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) {
BattleState s = targets[count++] = new BattleState(entity, e);
int hit = 0;
hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1);
s.setStyle(CombatStyle.MELEE);
s.setStyle(CombatStyle.MELEE);
s.setEstimatedHit(hit);
}
}
@ -96,13 +96,13 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements
@Override
public void visualize(Entity entity, Entity victim, BattleState state) {
playGlobalAudio(entity.getLocation(), Sounds.CLEAVE_2529);
entity.visualize(ANIMATION, GRAPHIC);
playGlobalAudio(entity.getLocation(), Sounds.CLEAVE_2529);
entity.visualize(ANIMATION, GRAPHIC);
}
@Override
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
if (state.getTargets() != null) {
if (state != null && state.getTargets() != null) {
for (BattleState s : state.getTargets()) {
if (s != null) {
s.getVictim().animate(victim.getProperties().getDefenceAnimation());

View file

@ -64,7 +64,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
BattleState[] targets = getTargets(entity, victim, state);
state.setTargets(targets);
for (BattleState s : targets) {
s.setStyle(CombatStyle.MELEE);
s.setStyle(CombatStyle.MELEE);
int hit = 0;
if (isAccurateImpact(entity, s.getVictim(), CombatStyle.MELEE)) {
hit = RandomFunction.random(calculateHit(entity, s.getVictim(), 1.1) + 1);
@ -90,7 +90,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
*/
private BattleState[] getTargets(Entity entity, Entity victim, BattleState state) {
if (!entity.getProperties().isMultiZone() || !victim.getProperties().isMultiZone()) {
return new BattleState[] { state };
return new BattleState[]{state};
}
Location vl = victim.getLocation();
int x = vl.getX();
@ -102,6 +102,9 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
if (n instanceof Familiar) {
continue;
}
if (!n.isAttackable(entity, CombatStyle.MELEE, false)) {
continue;
}
if (n.getLocation().equals(vl.transform(dir.getStepY(), dir.getStepX(), 0)) || n.getLocation().equals(vl.transform(-dir.getStepY(), -dir.getStepX(), 0))) {
l.add(new BattleState(entity, n));
if (l.size() >= 3) {
@ -127,7 +130,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
@Override
public void impact(Entity entity, Entity victim, BattleState state) {
if (state.getTargets() != null) {
if (state != null && state.getTargets() != null) {
for (BattleState s : state.getTargets()) {
if (s != null) {
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
@ -146,7 +149,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
@Override
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
if (state.getTargets() != null) {
if (state != null && state.getTargets() != null) {
for (BattleState s : state.getTargets()) {
if (s != null) {
s.getVictim().animate(victim.getProperties().getDefenceAnimation());

View file

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

View file

@ -129,6 +129,14 @@ class WaterSourceListener : InteractionListener {
FISHBOWL(
inputs = intArrayOf(Items.FISHBOWL_6667),
output = Items.FISHBOWL_6668
),
KETTLE(
inputs = intArrayOf(Items.KETTLE_7688),
output = Items.FULL_KETTLE_7690
),
CUP(
inputs = intArrayOf(Items.EMPTY_CUP_1980),
output = Items.CUP_OF_WATER_4458
);
companion object

View file

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

View file

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

View file

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

View file

@ -347,6 +347,7 @@ public final class HouseManager {
Room room = rooms[0][4][3] = new Room(RoomProperties.GARDEN);
room.configure(style);
room.getHotspots()[0].setDecorationIndex(0);
redecorate(HousingStyle.BASIC_WOOD);
this.location = location;
}

View file

@ -1,6 +1,8 @@
package content.global.skill.construction;
import core.api.Container;
import org.rs09.consts.Items;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.world.map.zone.MapZone;
@ -91,6 +93,7 @@ public final class HouseZone extends MapZone {
public boolean leave(Entity e, boolean logout) {
if (e instanceof Player) {
Player p = (Player) e;
remove_items(p);
// The below tears down the house if the owner was the one who left
if (house == p.getHouseManager()) {
house.expelGuests(p);
@ -121,4 +124,11 @@ public final class HouseZone extends MapZone {
}
return true;
}
private void remove_items(Player p) {
for (int item = Items.KETTLE_7688; item <= Items.CHEFS_DELIGHT_7755; item++) {//Removes all PoH versions of tea and beer barrel-related items
removeAll(p, item, Container.INVENTORY);
removeAll(p, item, Container.BoB);
}
}
}

View file

@ -1,76 +0,0 @@
package content.global.skill.construction.decoration;
import core.game.interaction.NodeUsageEvent;
import core.game.interaction.UseWithHandler;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.game.node.scenery.Scenery;
import core.game.world.update.flag.context.Animation;
import core.plugin.Initializable;
import core.plugin.Plugin;
import org.rs09.consts.Items;
/**
* Handles the Construction beer barrels.
* @author Splinter
*/
@Initializable
public class BeerBarrelPlugin extends UseWithHandler {
/**
* The object ids
*/
private static final int[] OBJECTS = new int[] {
13568, 13569, 13570, 13571, 13572, 13573
};
/**
* Constructs a new {@Code BeerBarrelPlugin} {@Code Object}
*/
public BeerBarrelPlugin() {
super(1919);
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
for (int id : OBJECTS) {
addHandler(id, OBJECT_TYPE, this);
}
return this;
}
@Override
public boolean handle(NodeUsageEvent event) {
Player player = event.getPlayer();
final Scenery object = (Scenery) event.getUsedWith();
if (player.getInventory().remove(new Item(Items.BEER_GLASS_1919))) {
player.animate(Animation.create(833));
player.sendMessage("You fill up your glass.");
player.getInventory().add(new Item(getReward(object.getId()), 1));
}
return true;
}
/**
* Get the beer reward based on the interaced barrel.
* @return the item to give.
*/
public int getReward(int barrelId) {
switch (barrelId) {
case 13568:
return 1917;
case 13569:
return 5763;
case 13570:
return 1905;
case 13571:
return 1909;
case 13572:
return 1911;
case 13573:
return 5755;
}
return 1917;
}
}

View file

@ -0,0 +1,78 @@
package content.global.skill.construction.decoration.kitchen
import core.api.addItem
import core.api.freeSlots
import core.api.sendDialogue
import core.game.dialogue.DialogueFile
import core.game.dialogue.Topic
import core.game.node.entity.player.Player
import core.tools.END_DIALOGUE
abstract class AbstractContainer(containerId: Int, containers: Map<Int,List<Pair<String, Int>>>, private val containerName: String) : DialogueFile() {
private val container = containers[containerId]!!
private val containerItemsNames = container.map { it.first }
private val containerItemsIds = container.map { it.second }
private val size = container.size
private val itemsPerPage = 4
private fun checkItem(player: Player, idx : Int){
val item = containerItemsIds[idx]
if (addItem(player, item)){
end()
}
else{
sendDialogue(player, "You need at least one free inventory space to take from the $containerName.").also { stage = END_DIALOGUE }
}
}
private fun createTopics(page: Int): MutableList<Topic<Int>> {
val startIndex = (page - 1) * itemsPerPage
val endIndex = (startIndex + itemsPerPage).coerceAtMost(size)
val topics = containerItemsNames.subList(startIndex, endIndex)
.mapIndexed { index, item -> Topic(item, startIndex + index + 1, skipPlayer = true) }
.toMutableList()
// Add "More" button if there's another page or to loop back
if (endIndex < size || page > 1) {
topics.add(Topic("More", endIndex + 1, skipPlayer = true))
}
return topics
}
override fun handle(componentID: Int, buttonID: Int) {
if (freeSlots(player!!) == 0) {
sendDialogue(player!!, "You need at least one free inventory space to take from the $containerName.").also { stage = END_DIALOGUE }
return
}
val itemsPerPage = 4
when (stage) {
0, size + 1 -> { // Initial stage or final more button clicked
val topics = createTopics(1)
showTopics(*topics.toTypedArray()).also { stage = 1 }
}
in 1..size -> {
val currentPage = (stage-1) / itemsPerPage
val startIndex = currentPage * itemsPerPage
if (buttonID == itemsPerPage + 1) { // "More" button clicked (but not the last one)
val topics = createTopics(currentPage + 1)
showTopics(*topics.toTypedArray()).also { stage = currentPage*itemsPerPage + 1 }
} else { // Handle item selection
val itemIndex = startIndex + (buttonID - 1)
if (itemIndex < size) {
checkItem(player!!, itemIndex)
}
}
}
else -> end()
}
}
}

View file

@ -0,0 +1,29 @@
package content.global.skill.construction.decoration.kitchen
import core.api.*
import core.game.interaction.InteractionListener
import org.rs09.consts.Items
class BeerBarrelInteraction : InteractionListener {
companion object{
private val barrelMap = mapOf(
org.rs09.consts.Scenery.BEER_BARREL_13568 to Items.BEER_7740,
org.rs09.consts.Scenery.CIDER_BARREL_13569 to Items.CIDER_7752,
org.rs09.consts.Scenery.ASGARNIAN_ALE_13570 to Items.ASGARNIAN_ALE_7744,
org.rs09.consts.Scenery.GREENMAN_S_ALE_13571 to Items.GREENMANS_ALE_7746,
org.rs09.consts.Scenery.DRAGON_BITTER_13572 to Items.DRAGON_BITTER_7748,
org.rs09.consts.Scenery.CHEF_S_DELIGHT_13573 to Items.CHEFS_DELIGHT_7754,
)
}
override fun defineListeners() {
onUseWith(SCENERY, Items.BEER_GLASS_7742, *barrelMap.keys.toIntArray()){ player, used, with ->
val drinkName = with.name.lowercase().replace("barrel", "").trim()+"."
if (removeItem(player, used)){
sendMessage(player, "You fill up your glass with $drinkName")
addItem(player, barrelMap[with.id]!!)
}
return@onUseWith true
}
}
}

View file

@ -1,77 +0,0 @@
package content.global.skill.construction.decoration.kitchen;
import core.game.interaction.NodeUsageEvent;
import core.game.interaction.UseWithHandler;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.game.node.scenery.Scenery;
import core.game.world.update.flag.context.Animation;
import core.plugin.Initializable;
import core.plugin.Plugin;
import org.rs09.consts.Items;
/**
* Handles the Construction beer barrels.
* @author Splinter
*/
@Initializable
public class BeerBarrelPlugin extends UseWithHandler {
/**
* The object ids
*/
private static final int[] OBJECTS = new int[] {
13568, 13569, 13570, 13571, 13572, 13573
};
/**
* Constructs a new {@Code BeerBarrelPlugin} {@Code Object}
*/
public BeerBarrelPlugin() {
super(1919);
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
for (int id : OBJECTS) {
addHandler(id, OBJECT_TYPE, this);
}
return this;
}
@Override
public boolean handle(NodeUsageEvent event) {
Player player = event.getPlayer();
final Scenery object = (Scenery) event.getUsedWith();
if (player.getInventory().remove(new Item(Items.BEER_GLASS_1919))) {
player.animate(Animation.create(3661 + (object.getId() - 13569)));
player.sendMessage("You fill up your glass with " + object.getName().toLowerCase().replace("barrel", "").trim() + ".");
player.getInventory().add(new Item(getReward(object.getId()), 1));
}
return true;
}
/**
* Get the beer reward based on the interacted barrel.
* @return the item to give.
*/
public int getReward(int barrelId) {
switch (barrelId) {
case 13568:
return 1917;
case 13569:
return 5763;
case 13570:
return 1905;
case 13571:
return 1909;
case 13572:
return 1911;
case 13573:
return 5755;
}
return 1917;
}
}

View file

@ -0,0 +1,42 @@
package content.global.skill.construction.decoration.kitchen
import core.api.openDialogue
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import org.rs09.consts.Items
import org.rs09.consts.Scenery
class LarderListener : InteractionListener {
companion object {
private val wooden_larder_items = listOf(
"Tea Leaves" to Items.TEA_LEAVES_7738,
"Bucket of Milk" to Items.BUCKET_OF_MILK_1927
)
private val oak_larder_items = wooden_larder_items + listOf(
"Eggs" to Items.EGG_1944,
"Pot of Flour" to Items.POT_OF_FLOUR_1933
)
private val teak_larder_items = oak_larder_items + listOf(
"Potatoes" to Items.POTATO_1942,
"Garlic" to Items.GARLIC_1550,
"Onions" to Items.ONION_1957,
"Cheese" to Items.CHEESE_1985
)
val larders = mapOf(
Scenery.LARDER_13565 to wooden_larder_items,
Scenery.LARDER_13566 to oak_larder_items,
Scenery.LARDER_13567 to teak_larder_items
)
}
override fun defineListeners() {
on(larders.keys.toIntArray(), IntType.SCENERY, "Search") { player, node ->
openDialogue(player, LarderDialogue(node.id))
return@on true
}
}
class LarderDialogue(shelfId: Int) : AbstractContainer(shelfId, larders, "larder")
}

View file

@ -1,149 +0,0 @@
package content.global.skill.construction.decoration.kitchen;
import core.cache.def.impl.SceneryDefinition;
import core.plugin.Initializable;
import core.game.dialogue.DialoguePlugin;
import core.game.interaction.OptionHandler;
import core.game.node.Node;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.plugin.Plugin;
import core.plugin.ClassScanner;
import org.rs09.consts.Items;
/**
* Handles the interactions for the three Larders.
* @author Splinter
*/
@Initializable
public final class LarderPlugin extends OptionHandler {
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ClassScanner.definePlugin(new LarderDialogue());
SceneryDefinition.forId(13565).getHandlers().put("option:search", this);
SceneryDefinition.forId(13566).getHandlers().put("option:search", this);
SceneryDefinition.forId(13567).getHandlers().put("option:search", this);
return this;
}
@Override
public boolean handle(Player player, Node node, String option) {
player.getDialogueInterpreter().open(42048, node.getId());
return true;
}
/**
* Dialogue options for the Larders.
* @author Splinter
* @version 1.0
*/
public final class LarderDialogue extends DialoguePlugin {
/**
* Constructs a new {@code LarderDialogue} {@code Object}.
*/
public LarderDialogue() {
/**
* empty.
*/
}
/**
* Constructs a new {@code LarderDialogue} {@code Object}.
*
* @param player
* the player.
*/
public LarderDialogue(Player player) {
super(player);
}
@Override
public DialoguePlugin newInstance(Player player) {
return new LarderDialogue(player);
}
@Override
public boolean open(Object... args) {
int id = (int) args[0];
switch (id) {
case 13565:
interpreter.sendOptions("Select an Option", "Tea Leaves", "Bucket of Milk");
stage = 1;
break;
case 13566:
interpreter.sendOptions("Select an Option", "Tea Leaves", "Bucket of Milk", "Eggs", "Pot of Flour");
stage = 1;
break;
case 13567:
interpreter.sendOptions("Select an Option", "Tea Leaves", "Bucket of Milk", "Eggs", "Pot of Flour", "More Options");
stage = 1;
break;
}
return true;
}
@Override
public boolean handle(int interfaceId, int buttonId) {
if (player.getInventory().freeSlots() < 1) {
player.sendMessage("You need at least one free inventory space to take from the larder.");
end();
return true;
}
switch (stage) {
case 1:
switch (buttonId) {
case 1:
player.getInventory().add(new Item(Items.TEA_LEAVES_7738, 1));
end();
break;
case 2:
player.getInventory().add(new Item(Items.BUCKET_OF_MILK_1927, 1));
end();
break;
case 3:
player.getInventory().add(new Item(Items.EGG_1944, 1));
end();
break;
case 4:
player.getInventory().add(new Item(Items.POT_OF_FLOUR_1933, 1));
end();
break;
case 5:
player.getDialogueInterpreter().sendOptions(
"Select an Option", "Potatoes", "Garlic", "Onions", "Cheese");
stage = 2;
break;
}
break;
case 2:
switch (buttonId) {
case 1:
player.getInventory().add(new Item(Items.POTATO_1942, 1));
end();
break;
case 2:
player.getInventory().add(new Item(Items.GARLIC_1550, 1));
end();
break;
case 3:
player.getInventory().add(new Item(Items.ONION_1957, 1));
end();
break;
case 4:
player.getInventory().add(new Item(Items.CHEESE_1985, 1));
end();
break;
}
}
return true;
}
@Override
public int[] getIds() {
return new int[] { 42048 };
}
}
}

View file

@ -0,0 +1,97 @@
package content.global.skill.construction.decoration.kitchen
import core.api.*
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import org.rs09.consts.Items
import org.rs09.consts.Scenery
class ShelfListener : InteractionListener {
companion object {
// Source for text https://www.youtube.com/watch?v=SNuqelEft3Y
private val woodshelf_1_items = listOf(
"Kettle" to Items.KETTLE_7688,
"Teapot" to Items.TEAPOT_7702,
"Cup" to Items.EMPTY_CUP_7728
)
private val woodshelf_2_items = woodshelf_1_items + listOf(
"Beer glass" to Items.BEER_GLASS_7742
)
private val woodshelf_3_items = woodshelf_2_items.map { (name, itemId) ->
when (name) {
"Cup" -> {
"Porcelain cup" to Items.PORCELAIN_CUP_7732
}
"Teapot" -> {
"Teapot" to Items.TEAPOT_7714
}
else -> {
name to itemId
}
}
} + listOf("Cake tin" to Items.CAKE_TIN_1887)
private val oakshelf_1_items = woodshelf_2_items + listOf("Cake tin" to Items.CAKE_TIN_1887) + listOf("Bowl" to Items.BOWL_1923)
private val oakshelf_2_items = oakshelf_1_items.map { (name, itemId) ->
when (name) {
"Cup" -> {
"Porcelain cup" to Items.PORCELAIN_CUP_7732
}
"Teapot" -> {
"Teapot" to Items.TEAPOT_7714
}
else -> {
name to itemId
}
}
} + listOf("Pie dish" to Items.PIE_DISH_2313)
private val teakshelf_1_items = oakshelf_2_items + listOf(
"Pot" to Items.EMPTY_POT_1931
)
private val teakshelf_2_items = teakshelf_1_items.map { (name, itemId) ->
when (name) {
"Porcelain cup" -> {
"Porcelain cup" to Items.PORCELAIN_CUP_7735
}
"Teapot" -> {
"Teapot" to Items.TEAPOT_7726
}
else -> {
name to itemId
}
}
} + listOf("Chef's Hat" to Items.CHEFS_HAT_1949)
val shelves = mapOf(
Scenery.SHELVES_13545 to woodshelf_1_items,
Scenery.SHELVES_13546 to woodshelf_2_items,
Scenery.SHELVES_13547 to woodshelf_3_items,
Scenery.SHELVES_13548 to oakshelf_1_items,
Scenery.SHELVES_13549 to oakshelf_2_items,
Scenery.SHELVES_13550 to teakshelf_1_items,
Scenery.SHELVES_13551 to teakshelf_2_items,
)
}
override fun defineListeners() {
on(shelves.keys.toIntArray(), IntType.SCENERY, "Search") { player, node ->
openDialogue(player, ShelfDialogue(node.id))
return@on true
}
}
class ShelfDialogue(shelfId: Int) : AbstractContainer(shelfId, shelves, "shelf")
}

View file

@ -1,239 +0,0 @@
package content.global.skill.construction.decoration.kitchen;
import core.cache.def.impl.SceneryDefinition;
import core.game.dialogue.DialoguePlugin;
import core.game.interaction.OptionHandler;
import core.game.node.Node;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.plugin.Initializable;
import core.plugin.Plugin;
import core.plugin.ClassScanner;
import org.rs09.consts.Items;
/**
* Handles the shelves in the kitchen room.
* @author Splinter
*/
@Initializable
public final class ShelfPlugin extends OptionHandler {
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ClassScanner.definePlugin(new ShelfDialogue());
for (int i = 13545; i < 13552; i++) {
SceneryDefinition.forId(i).getHandlers().put("option:search", this);
}
return this;
}
@Override
public boolean handle(Player player, Node node, String option) {
player.getDialogueInterpreter().open(778341, node.getId());
return true;
}
/**
* Dialogue options for the shelves, what a mess!
* @author Splinter
* @version 1.0
*/
public final class ShelfDialogue extends DialoguePlugin {
/**
* Constructs a new {@code ShelfDialogue} {@code Object}.
*/
public ShelfDialogue() {
/**
* empty.
*/
}
/**
* Constructs a new {@code ShelfDialogue} {@code Object}.
* @param player the player.
*/
public ShelfDialogue(Player player) {
super(player);
}
@Override
public DialoguePlugin newInstance(Player player) {
return new ShelfDialogue(player);
}
@Override
public boolean open(Object... args) {
int id = (int) args[0];
switch (id) {
case 13545:// wood 1
interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Clay cup");
stage = 1;
break;
case 13546:// wood 2
interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Clay cup", "Empty beer glass");
stage = 1;
break;
case 13547:// wood 3
interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Clay cup", "Empty beer glass", "Cake tin");
stage = 1;
break;
case 13548:// oak 1
interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Clay cup", "Empty beer glass", "Bowl");
stage = 2;
break;
case 13549:// oak 2
interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Porcelain cup", "Empty beer glass", "More Options");
stage = 3;
break;
case 13550:// teak 1
case 13551:
interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Porcelain cup", "Empty beer glass", "More Options");
stage = 5;
break;
}
return true;
}
@Override
public boolean handle(int interfaceId, int buttonId) {
if (player.getInventory().freeSlots() < 1) {
player.sendMessage("You need at least one free inventory space to take from the shelves.");
end();
return true;
}
switch (stage) {
case 1:// all wood shelves
switch (buttonId) {
case 1:
end();
player.getInventory().add(new Item(Items.KETTLE_7688, 1));
break;
case 2:
end();
player.getInventory().add(new Item(Items.TEAPOT_7702, 1));
break;
case 3:
end();
player.getInventory().add(new Item(Items.EMPTY_CUP_7728, 1));
break;
case 4:
end();
player.getInventory().add(new Item(Items.BEER_GLASS_1919, 1));
break;
case 5:
end();
player.getInventory().add(new Item(Items.CAKE_TIN_1887, 1));
break;
}
break;
case 2:// Oak shelf #1
switch (buttonId) {
case 1:
end();
player.getInventory().add(new Item(Items.KETTLE_7688, 1));
break;
case 2:
end();
player.getInventory().add(new Item(Items.TEAPOT_7702, 1));
break;
case 3:
end();
player.getInventory().add(new Item(Items.EMPTY_CUP_7728, 1));
break;
case 4:
end();
player.getInventory().add(new Item(Items.BEER_GLASS_1919, 1));
break;
case 5:
end();
player.getInventory().add(new Item(Items.BOWL_1923, 1));
break;
}
break;
case 3:// Oak shelves #2 only
switch (buttonId) {
case 1:
end();
player.getInventory().add(new Item(Items.KETTLE_7688, 1));
break;
case 2:
end();
player.getInventory().add(new Item(Items.TEAPOT_7702, 1));
break;
case 3:
end();
player.getInventory().add(new Item(Items.PORCELAIN_CUP_4244, 1));
break;
case 4:
end();
player.getInventory().add(new Item(Items.BEER_GLASS_1919, 1));
break;
case 5:
interpreter.sendOptions("Select an Option", "Bowl", "Cake tin");
stage = 4;
break;
}
break;
case 4:// Oak shelves #2 only
switch (buttonId) {
case 1:
end();
player.getInventory().add(new Item(Items.BOWL_1923, 1));
break;
case 2:
end();
player.getInventory().add(new Item(Items.CAKE_TIN_1887, 1));
break;
}
case 5:// teak shelves
switch (buttonId) {
case 1:
end();
player.getInventory().add(new Item(Items.KETTLE_7688, 1));
break;
case 2:
end();
player.getInventory().add(new Item(Items.TEAPOT_7702, 1));
break;
case 3:
end();
player.getInventory().add(new Item(Items.PORCELAIN_CUP_7735, 1));
break;
case 4:
end();
player.getInventory().add(new Item(Items.BEER_GLASS_1919, 1));
break;
case 5:
interpreter.sendOptions("Select an Option", "Bowl", "Pie dish", "Empty pot");
stage = 6;
break;
}
break;
case 6:// teak shelves
switch (buttonId) {
case 1:
end();
player.getInventory().add(new Item(Items.BOWL_1923, 1));
break;
case 2:
end();
player.getInventory().add(new Item(Items.PIE_DISH_2313, 1));
break;
case 3:
end();
player.getInventory().add(new Item(Items.EMPTY_POT_1931, 1));
break;
}
break;
}
return true;
}
@Override
public int[] getIds() {
return new int[] { 778341 };
}
}
}

View file

@ -0,0 +1,88 @@
package content.global.skill.construction.decoration.kitchen
import core.api.*
import core.game.interaction.InteractionListener
import core.game.interaction.QueueStrength
import org.rs09.consts.Scenery as SceneryConst
import org.rs09.consts.Items
import core.game.node.scenery.Scenery
import core.tools.secondsToTicks
class StoveInteraction : InteractionListener {
// Imagine if these indexes incremented logically...
companion object{
val valid_stoves = intArrayOf(
SceneryConst.FIREPIT_WITH_HOOK_13529,
SceneryConst.FIREPIT_WITH_POT_13531,
SceneryConst.SMALL_OVEN_13533,
SceneryConst.LARGE_OVEN_13536,
SceneryConst.STEEL_RANGE_13539,
SceneryConst.FANCY_RANGE_13542
)
val in_progress_stoves = intArrayOf(
SceneryConst.FIREPIT_WITH_HOOK_13530,
SceneryConst.FIREPIT_WITH_POT_13532,
SceneryConst.SMALL_OVEN_13534,
SceneryConst.LARGE_OVEN_13537,
SceneryConst.STEEL_RANGE_13541,
SceneryConst.FANCY_RANGE_13544
)
val stoves_with_kettle = intArrayOf(
SceneryConst.FIREPIT_WITH_HOOK_13530,
SceneryConst.FIREPIT_WITH_POT_13532,
SceneryConst.SMALL_OVEN_13535,
SceneryConst.LARGE_OVEN_13538,
SceneryConst.STEEL_RANGE_13540,
SceneryConst.FANCY_RANGE_13543
)
const val kettleBoiled = "kettle_boiled"
}
override fun defineListeners() {
onUseWith(SCENERY, Items.FULL_KETTLE_7690, *valid_stoves){ player, used, with ->
val idx = valid_stoves.indexOf(with.id)
if (removeItem(player, used)){
replaceScenery(with as Scenery, in_progress_stoves[idx], -1)
sendMessage(player, "You place the kettle over the fire.")
setAttribute(player, kettleBoiled, 1)
queueScript(player, secondsToTicks(10), QueueStrength.SOFT){ _ ->
sendMessage(player, "The kettle boils.")
// Of course they changed how these items work so we need 2 different cases
setAttribute(player, kettleBoiled, 2)
if (with.id in intArrayOf(SceneryConst.FIREPIT_WITH_HOOK_13529, SceneryConst.FIREPIT_WITH_POT_13531))
return@queueScript stopExecuting(player)
else{
getScenery(with.location)?.let { replaceScenery(it, stoves_with_kettle[idx], -1) }
return@queueScript stopExecuting(player)
}
}
}
return@onUseWith true
}
on(stoves_with_kettle, SCENERY, "take-kettle"){ player, node ->
if (getAttribute(player, kettleBoiled, 0) == 0){
sendMessage(player, "This is not your kettle.")
return@on false
}
else if (getAttribute(player, kettleBoiled, 0) == 1){
sendMessage(player, "The kettle has not boiled yet.")
return@on false
}
if(addItem(player, Items.HOT_KETTLE_7691)){
replaceScenery(node as Scenery, valid_stoves[stoves_with_kettle.indexOf(node.id)], -1)
setAttribute(player, kettleBoiled, 0)
return@on true
}
else {
sendMessage(player, "You do not have space to do that.")
return@on false
}
}
}
}

View file

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

View file

@ -1,171 +0,0 @@
package content.global.skill.construction.decoration.workshop;
import core.cache.def.impl.ItemDefinition;
import core.cache.def.impl.SceneryDefinition;
import core.plugin.Initializable;
import core.game.dialogue.DialogueInterpreter;
import core.game.dialogue.DialoguePlugin;
import core.game.interaction.OptionHandler;
import core.game.node.Node;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.game.node.scenery.Scenery;
import core.plugin.Plugin;
import core.plugin.ClassScanner;
import java.util.ArrayList;
import java.util.List;
/**
* Handles the 5 types of tool stores
* ToolsPlugin.java
* @author Clayton Williams
* @date Oct 29, 2015
*
*/
@Initializable
public class ToolsPlugin extends OptionHandler {
/**
* The different tool stores
* @author Clayton Williams
* @date Oct 29, 2015
*/
private enum ToolStore {
TOOLSTORE_1(13699, 8794, 1755, 2347, 1735),
TOOLSTORE_2(13700, 1925, 952, 590),
TOOLSTORE_3(13701, 1757, 1785, 1733),
TOOLSTORE_4(13702, 1595, 1597, 1592, 1599, 5523),
TOOLSTORE_5(13703, 5341, 952, 676, 5343, 5331);
/**
* The object id of the tool store
*/
private int objectId;
/**
* The tools included
*/
private int[] tools;
/**
* ToolStore
* @param objectId
* @param tools
*/
ToolStore(int objectId, int... tools) {
this.objectId = objectId;
this.tools = tools;
}
/**
* Gets the toolstore from an object id
* @param objectId - the object id
* @return
*/
private static ToolStore forId(int objectId) {
for (ToolStore t : ToolStore.values()) {
if (t.objectId == objectId) {
return t;
}
}
return null;
}
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
for (ToolStore t : ToolStore.values()) {
SceneryDefinition.forId(t.objectId).getHandlers().put("option:search", this);
}
ClassScanner.definePlugin(new ToolDialogue());
return this;
}
@Override
public boolean handle(Player player, Node node, String option) {
Scenery object = node.asScenery();
ToolStore ts = ToolStore.forId(object.getId());
if (ts != null) {
player.getDialogueInterpreter().open(DialogueInterpreter.getDialogueKey("con:tools"), ts);
}
return true;
}
/**
* Handles the tool dialogue
* @author Clayton Williams
* @date Oct 29, 2015
*/
private final class ToolDialogue extends DialoguePlugin {
/**
* The tool store being used
*/
private ToolStore toolStore;
/**
* ToolDialogue
*/
private ToolDialogue() {
/**
* Empty
*/
}
/**
* ToolDialogue
* @param player
*/
public ToolDialogue(Player player) {
super(player);
}
@Override
public DialoguePlugin newInstance(Player player) {
return new ToolDialogue(player);
}
@Override
public boolean open(Object... args) {
toolStore = (ToolStore) args[0];
List<String> itemNames = new ArrayList<String>();
for(int itemId : toolStore.tools) {
ItemDefinition n = ItemDefinition.forId(itemId);
itemNames.add(n.getName());
}
interpreter.sendOptions("Select a Tool", itemNames.toArray(new String[itemNames.size()]));
stage = 1;
return true;
}
@Override
public boolean handle(int interfaceId, int buttonId) {
switch (stage) {
case 1:
Item item = new Item(toolStore.tools[buttonId - 1], 1);
if (player.getInventory().freeSlots() <= 0) {
interpreter.sendDialogue("You have no space in your inventory.");
stage = 2;
return true;
}
player.getInventory().add(item);
end();
return true;
case 2:
end();
return true;
}
return false;
}
@Override
public int[] getIds() {
return new int[] { DialogueInterpreter.getDialogueKey("con:tools") };
}
}
}

View file

@ -0,0 +1,233 @@
package content.global.skill.construction.decoration.workshop
import core.api.*
import core.cache.def.impl.SceneryDefinition
import core.game.dialogue.DialogueInterpreter
import core.game.dialogue.DialoguePlugin
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.player.Player
import core.plugin.ClassScanner.definePlugin
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.Items
/**
* Handles the 5 types of tool stores
* ToolsPlugin.java
* @author Clayton Williams
* @date Oct 29, 2015
*/
@Initializable
class ToolsPlugin : OptionHandler() {
val id =0
@Throws(Throwable::class)
override fun newInstance(arg: Any?): Plugin<Any?> {
definePlugin(ToolDialogue())
SceneryDefinition.forId(13699).getHandlers().put("option:search", this)
SceneryDefinition.forId(13700).getHandlers().put("option:search", this)
SceneryDefinition.forId(13701).getHandlers().put("option:search", this)
SceneryDefinition.forId(13702).getHandlers().put("option:search", this)
SceneryDefinition.forId(13703).getHandlers().put("option:search", this)
return this
}
override fun handle(player: Player, node: Node, option: String?): Boolean {
openDialogue(player,DialogueInterpreter.getDialogueKey("con:tools"), node.getId())
return true
}
/**
* Handles the tool dialogue
* @author Clayton Williams
* @date Oct 29, 2015
*/
private inner class ToolDialogue : DialoguePlugin {
private var id: Int? = null
/**
* Constructs a new `ToolDialogue` `Object`.
*/
constructor()
/**
* Constructs a new `ToolDialogue` `Object`.
*
* @param player
* the player.
*/
constructor(player: Player?) : super(player)
fun addItemMsg(player: Player, id: Int, amount: Int = 1) {
if (!addItem(player, id,amount)){
sendDialogue(player, "You have no space in your inventory.")
stage = 3 //Stage 3, player just needs to confirm they received the no inventory space dialogue
} else {end()} //Player received their Item, close dialogue
}
public override fun newInstance(player: Player?): DialoguePlugin {
return ToolDialogue(player)
}
override fun open(vararg args: Any?): Boolean {
id = args[0] as Int
when (id) {
13699 -> {
sendDialogueOptions(player,"Select a Tool", "Saw", "Chisel", "Hammer", "Shears")
}
13700 -> {
sendDialogueOptions(player,"Select a Tool", "Bucket", "Knife", "Spade", "Tinderbox")
}
13701 -> {
sendDialogueOptions(player,"Select a Tool", "Brown apron", "Glassblowing pipe", "Needle")
}
13702 -> {
sendDialogueOptions(player,"Select a Tool","Amulet mould","Necklace mould","Ring mould","Holy mould","More Options"
)
stage = 1
}
13703 -> {
sendDialogueOptions(player,"Select a Tool", "Rake", "Spade", "Trowel", "Seed dibber", "Watering can")
}
}
return true
}
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
if (stage == 3) {//If reply was on full inventory confirmation, close dialogue
end()
return true
}
when (id) {
13699 -> {
when (buttonId) {
1 -> {
addItemMsg(player, Items.SAW_8794)
}
2 -> {
addItemMsg(player, Items.CHISEL_1755)
}
3 -> {
addItemMsg(player, Items.HAMMER_2347)
}
4 -> {
addItemMsg(player, Items.SHEARS_1735)
}
}
}
13700 -> {
when (buttonId) {
1 -> {
addItemMsg(player, Items.BUCKET_1925)
}
2 -> {
addItemMsg(player, Items.KNIFE_946)
}
3 -> {
addItemMsg(player, Items.SPADE_952)
}
4 -> {
addItemMsg(player, Items.TINDERBOX_590)
}
}
}
13701 -> {
when (buttonId) {
1 -> {
addItemMsg(player, Items.BROWN_APRON_1757)
}
2 -> {
addItemMsg(player, Items.GLASSBLOWING_PIPE_1785)
}
3 -> {
addItemMsg(player, Items.NEEDLE_1733)
}
}
}
13702 -> {
when (stage) {
1 -> when (buttonId) {
1 -> {
addItemMsg(player, Items.AMULET_MOULD_1595)
}
2 -> {
addItemMsg(player, Items.NECKLACE_MOULD_1597)
}
3 -> {
addItemMsg(player, Items.RING_MOULD_1592)
}
4 -> {
addItemMsg(player, Items.HOLY_MOULD_1599)
}
5 -> {
sendDialogueOptions(player,
"Select a Tool", "Bracelet mould", "Tiara mould"
)
stage = 2
}
}
2 -> {
when (buttonId) {
1 -> {
addItemMsg(player, Items.BRACELET_MOULD_11065)
}
2 -> {
addItemMsg(player, Items.TIARA_MOULD_5523)
}
}
}
}
}
13703 -> {
when (buttonId) {
1 -> {
addItemMsg(player, Items.RAKE_5341)
}
2 -> {
addItemMsg(player, Items.SPADE_952)
}
3 -> {
addItemMsg(player, Items.TROWEL_676)
}
4 -> {
addItemMsg(player, Items.SEED_DIBBER_5343)
}
5 -> {
addItemMsg(player, Items.WATERING_CAN_5331)
}
}
}
}
return true
}
override fun getIds(): IntArray {
return intArrayOf(DialogueInterpreter.getDialogueKey("con:tools"))
}
}
}

View file

@ -81,7 +81,7 @@ public enum CookableItems {
// bowl foods
BOWL_STEW(2003, 2001, 2005, 25, 117, 68, 392, 68, 392),
BOWL_CURRY(2011, 2009, 2013, 60, 280, 38, 332, 38, 332),
BOWL_NETTLE(4239, 4237, 4239, 20, 52, 78, 412, 78, 412),
BOWL_NETTLE(4239, 4237, 4239, 20, 53, 78, 412, 78, 412),
BOWL_EGG(7078, 7076, 7090, 13, 50, 0, 0, 90, 438),
BOWL_ONION(7084, 1871, 7092, 43, 60, 36, 322, 36, 322),
BOWL_MUSHROOM(7082, 7080, 7094, 46, 60, 16, 282, 16, 282),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,166 @@
package content.global.skill.cooking
import core.api.*
import core.game.interaction.InteractionListener
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import org.rs09.consts.Items
/**
* This deals with all tea mixtures
* cup of tea
* nettle tea
* nettle water
* nettle tea (cup)
* poh tea
* clay
* porcelain
* gold trim
* Flask
*/
class TeaInteraction : InteractionListener {
private val teaMilkMap = mapOf(
Items.NETTLE_TEA_4239 to Items.NETTLE_TEA_4240,
Items.CUP_OF_TEA_4242 to Items.CUP_OF_TEA_4243,
Items.CUP_OF_TEA_4245 to Items.CUP_OF_TEA_4246,
Items.CUP_OF_TEA_7730 to Items.CUP_OF_TEA_7731,
Items.CUP_OF_TEA_7733 to Items.CUP_OF_TEA_7734,
Items.CUP_OF_TEA_7736 to Items.CUP_OF_TEA_7737,
)
private val teaCupMap = mapOf(
Items.NETTLE_TEA_4239 to Items.CUP_OF_TEA_4242,
Items.NETTLE_TEA_4240 to Items.CUP_OF_TEA_4243,
Items.BOWL_OF_HOT_WATER_4456 to Items.CUP_OF_HOT_WATER_4460
)
// This is specifically for the porcelain cup without a handle for Ghost Ahoy
private val teaCupPorcelainMap = mapOf(
Items.NETTLE_TEA_4239 to Items.CUP_OF_TEA_4245,
Items.NETTLE_TEA_4240 to Items.CUP_OF_TEA_4246
)
private val emptyTeaPot = intArrayOf(
Items.TEAPOT_7702,
Items.TEAPOT_7714,
Items.TEAPOT_7726
)
private val leafTeaPot = intArrayOf(
Items.TEAPOT_WITH_LEAVES_7700,
Items.TEAPOT_WITH_LEAVES_7712,
Items.TEAPOT_WITH_LEAVES_7724
)
private val filledTeaPot = intArrayOf(
Items.POT_OF_TEA_4_7692,
Items.POT_OF_TEA_3_7694,
Items.POT_OF_TEA_2_7696,
Items.POT_OF_TEA_1_7698,
Items.POT_OF_TEA_4_7704,
Items.POT_OF_TEA_3_7706,
Items.POT_OF_TEA_2_7708,
Items.POT_OF_TEA_1_7710,
Items.POT_OF_TEA_4_7716,
Items.POT_OF_TEA_3_7718,
Items.POT_OF_TEA_2_7720,
Items.POT_OF_TEA_1_7722,
)
private val emptyBuilderCup = intArrayOf(
Items.EMPTY_CUP_7728,
Items.PORCELAIN_CUP_7732,
Items.PORCELAIN_CUP_7735
)
override fun defineListeners() {
onUseWith(ITEM, teaMilkMap.keys.toIntArray(), Items.BUCKET_OF_MILK_1927){ player, used, with ->
if (removeItem(player, used) && removeItem(player, with)){
addItem(player, teaMilkMap[used.id]!!)
addItem(player, Items.BUCKET_1925)
}
return@onUseWith true
}
onUseWith(ITEM, teaCupMap.keys.toIntArray(), Items.EMPTY_CUP_1980){ player, used, with ->
if (used.id != Items.BOWL_OF_HOT_WATER_4456){
if (getDynLevel(player, Skills.COOKING) < 20){
sendDialogue(player, "You need a cooking level of 20 to make tea.")
return@onUseWith false
}
}
if (removeItem(player, used) && removeItem(player, with)) {
addItem(player, teaCupMap[used.id]!!)
addItem(player, Items.BOWL_1923)
}
return@onUseWith true
}
onUseWith(ITEM, Items.BOWL_OF_WATER_1921, Items.NETTLES_4241) { player, used, with ->
if (removeItem(player, used) && removeItem(player, with)){
addItem(player, Items.NETTLE_WATER_4237)
}
return@onUseWith true
}
onUseWith(ITEM, teaCupPorcelainMap.keys.toIntArray(), Items.PORCELAIN_CUP_4244){player, used, with ->
if (getDynLevel(player, Skills.COOKING) < 20){
sendDialogue(player, "You need a cooking level of 20 to make tea.")
return@onUseWith false
}
if(removeItem(player, used) && removeItem(player, with)){
addItem(player, teaCupPorcelainMap[used.id]!!)
addItem(player, Items.BOWL_1923)
}
return@onUseWith true
}
onUseWith(ITEM, emptyTeaPot, Items.TEA_LEAVES_7738) { player, used, with ->
if (removeItem(player, used) && removeItem(player, with)){
addItem(player, used.id - 2)
}
return@onUseWith true
}
onUseWith(ITEM, leafTeaPot, Items.HOT_KETTLE_7691) { player, used, with ->
if (getDynLevel(player, Skills.COOKING) < 20){
sendDialogue(player,"You need to be level 20 to make tea.")
return@onUseWith false
}
if (removeItem(player, used) && removeItem(player, with)){
addItem(player, Items.KETTLE_7688)
addItem(player, used.id - 8)
rewardXP(player, Skills.COOKING, 53.0)
}
return@onUseWith true
}
onUseWith(ITEM, filledTeaPot, *emptyBuilderCup) { player, used, with ->
return@onUseWith fillBuildersTea(player, used, with)
}
}
private fun fillBuildersTea(player: Player, used: Node, with: Node) : Boolean {
if (removeItem(player, used) && removeItem(player, with)){
// Thanks tea pot with tea leaves
if (used.id + 4 in emptyTeaPot){
addItem(player, used.id + 4)
}
else{
addItem(player, used.id + 2)
}
// Why do these cups have a noted form!
if (with.id == Items.EMPTY_CUP_7728){
addItem(player, with.id + 2)
}
else{
addItem(player, with.id + 1)
}
return true
}
return false
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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